Skip to content

feat: implement tagging rest api tag object [FC-0030]#74

Merged
bradenmacdonald merged 1 commit intoopenedx:mainfrom
open-craft:rpenido/fal-3474-implement_tagging_rest_api_tag_object
Sep 1, 2023
Merged

feat: implement tagging rest api tag object [FC-0030]#74
bradenmacdonald merged 1 commit intoopenedx:mainfrom
open-craft:rpenido/fal-3474-implement_tagging_rest_api_tag_object

Conversation

@rpenido
Copy link
Contributor

@rpenido rpenido commented Aug 21, 2023

Description

This PR implements the tagging REST API for updating object tags.

Supporting Information

Testing instructions

  • Ensure that the tests cover the expected behavior of the view as described in the related issue.

Private-ref: FAL-3474

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Aug 21, 2023
@openedx-webhooks
Copy link

openedx-webhooks commented Aug 21, 2023

Thanks for the pull request, @rpenido! Please note that it may take us up to several weeks or months to complete a review and merge your PR.

Feel free to add as much of the following information to the ticket as you can:

  • supporting documentation
  • Open edX discussion forum threads
  • timeline information ("this must be merged by XX date", and why that is)
  • partner information ("this is a course on edx.org")
  • any other information that can help Product understand the context for the PR

All technical communication about the code itself will be done via the GitHub pull request interface. As a reminder, our process documentation is here.

Please let us know once your PR is ready for our review and all tests are green.

Copy link
Contributor

@ChrisChV ChrisChV left a comment

Choose a reason for hiding this comment

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

Thanks @rpenido!

I've requested small changes, but once that's done it's good to go into @bradenmacdonald review.

  • I ran the tests
  • I read through the code
  • Includes documentation for the added REST API

assert response.status_code == expected_status

@ddt.data(
(None, "language_taxonomy", ["Portuguese"], status.HTTP_403_FORBIDDEN),
Copy link
Contributor

Choose a reason for hiding this comment

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

@rpenido Could you add a comment describing each case? It's easy to get lost here, comments can help :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @ChrisChV.
Could you please give me your opinion on this (5bf5de8) ?
The tests are exhaustive: I test the same condition for all users (None, "user", and "staff"), all taxonomies, and all conditions (single, empty, multiple, invalid..).

After writing the comments, it's clear that we have redundant tests.
Should I clear some of them?

Copy link
Contributor

@ChrisChV ChrisChV Aug 23, 2023

Choose a reason for hiding this comment

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

I think you can delete the test that use language_taxonomy, I think are the same of enabled_taxonomy

Copy link
Contributor

Choose a reason for hiding this comment

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

@rpenido I was thinking, and if we separate and group the tests in different functions? For example:

  • test_add_tag: with the three user types and taxonomy types
  • test_add_invalid_tags: with the three user types and taxonomy types
  • test_clear_tags: with the three user types and taxonomy types
  • Etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think you can delete the test that use language_taxonomy, I think are the same of enabled_taxonomy

I'm using the language taxonomy to test the required=true case

@rpenido I was thinking, and if we separate and group the tests in different functions? For example:

* `test_add_tag`: with the three user types and taxonomy types

* `test_add_invalid_tags`: with the three user types and taxonomy types

* `test_clear_tags`: with the three user types and taxonomy types

* Etc.

I liked it. Let me know what you think.

@bradenmacdonald bradenmacdonald changed the title feat: implement tagging rest api tag object feat: implement tagging rest api tag object [FC-0030] Aug 22, 2023
@bradenmacdonald bradenmacdonald added the FC Relates to an Axim Funded Contribution project label Aug 22, 2023
@rpenido rpenido marked this pull request as ready for review August 23, 2023 17:43
"""
Taxonomy admins can create or modify object tags on enabled taxonomies.
Everyone can potentially create/edit object tags (taxonomy=None). The object permission must be checked
to determine if the user can create/edit a object_tag for a specific taxonomy.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we need to emphasize this more, if I'm understanding it correctly. Something like:

Everyone can potentially create/edit object tags, IF they have permission
to use the taxonomy that the tag comes from, and IF they have permission
to edit the object in question.

❗This function only checks if they have permission to use the taxonomy! You
need to combine this with a separate check for their permission to edit the
object, which depends on the type of object.

The permissions for the taxonomy usage are simple: Everyone can create
or modify object tags using an enabled taxonomy. Only taxonomy admins
can create or modify object tags using a disabled taxonomies.

But I'm also wondering, why are admins allowed to create object tags using disabled taxonomies? Do we actually need that?

Copy link
Contributor Author

@rpenido rpenido Aug 24, 2023

Choose a reason for hiding this comment

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

It is a bit different. Because we are using the DjangoObjectPermission in the view, this function is called two times when we make a rest call:

  1. First, for checking if the user has the METHOD permission (PUT, in this specific case). In this call, we have taxonomy=None.
  2. After that, we call this function passing a taxonomy to check if the user has permission for this taxonomy

Everyone passes on 1. This is what I wanted to say with Everyone can potentially create/edit object tags (taxonomy=None).. I was trying to justify the if not taxonomy: return True

This is an implementation detail. Do you think we should let this out of the docstring?

This text covers the actual permission as of today:

Everyone can create or modify object tags using an enabled taxonomy. Only taxonomy admins
can create or modify object tags using a disabled taxonomy.

But I'm also wondering, why are admins allowed to create object tags using disabled taxonomies? Do we actually need that?

I'm not sure. It makes sense to me to let an admin clear the tags from a disabled taxonomy, for example.

Copy link
Contributor

@bradenmacdonald bradenmacdonald Aug 25, 2023

Choose a reason for hiding this comment

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

@rpenido That makes sense, thanks. Can you please explain that in the code, either in the docstring or in comments? It wasn't super clear to me what's happening until you replied.

This text covers the actual permission as of today:

But also we need to remind developers to check the object permissions too. We can't let just anyone apply object tags to a course, if they don't also have permission to edit that course in Studio!

Copy link
Contributor

@bradenmacdonald bradenmacdonald Aug 25, 2023

Choose a reason for hiding this comment

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

Actually, how will that work with this API? Does it have a way of checking if the user has edit permission for the object? (If not, we probably need to make the "edit object tags" API python-only, so that more specific REST APIs like in edx-platform can check user edit permissions before updating an object). Or we need to make this a base class that can be inherited and customized e.g. in edx-platform but not used directly, since it doesn't know how to check permissions. Or we need to add a pluggable permissions check.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rpenido That makes sense, thanks. Can you please explain that in the code, either in the docstring or in comments? It wasn't super clear to me what's happening until you replied.

Sure @bradenmacdonald! I'll work on this.

Or we need to make this a base class that can be inherited and customized e.g. in edx-platform but not used directly, since it doesn't know how to check permissions. Or we need to add a pluggable permissions check.

I think this is the strategy: inherit this class in edx-platform to enhance the permission.

About the object permission, look at the discussion in the issue here. Maybe I oversimplified reducing the permission check to only taxonomy.

In the openedx-learning side, as a stand-alone app, we basically apply tags to "strings" without any object knowledge.

I agree that we can have a callback to check some type of "Object" permission, but I'm not sure if we need this for the MVP.

Maybe aggregating more permissions in edx-platform (not on Object level, but Taxonomy level) is enough?

CC @ChrisChV

@bradenmacdonald
Copy link
Contributor

Let me know when you want a full review of this.

@rpenido
Copy link
Contributor Author

rpenido commented Aug 24, 2023

I think it's ready for a review @bradenmacdonald!

@bradenmacdonald
Copy link
Contributor

@rpenido I went to review but I'm getting hung up on the permissions issue. We can't allow users to tag any objects they want; they can only tag objects that they have permission to edit. So I think this API needs to be an abstract class that is not actually accessible, and then in edx-platform we can inherit it and implement a "get/set content object tags" API that uses this same code but adds appropriate permissions checks like "is this user allowed to edit this XBlock". What do you think?

@rpenido
Copy link
Contributor Author

rpenido commented Aug 25, 2023

@rpenido I went to review but I'm getting hung up on the permissions issue. We can't allow users to tag any objects they want; they can only tag objects that they have permission to edit. So I think this API needs to be an abstract class that is not actually accessible, and then in edx-platform we can inherit it and implement a "get/set content object tags" API that uses this same code but adds appropriate permissions checks like "is this user allowed to edit this XBlock". What do you think?

I agree with you. But with YAGNI in mind, I think that the MVP permission model is broad enough to allow this (if a user can create a course, it can tag objects). But I wouldn't want to make assumptions without checking. Can you help us here with the requirements @ChrisChV?

@bradenmacdonald
Copy link
Contributor

@rpenido
Here's what I'd like to see:

  • The docstring for this new "tag object" endpoint declares it as abstract, and explains why - it doesn't know how to check object permissions. It should not appear in urls.py
  • In edx-platform we'll subclass this endpoint, and add a very simple permissions check: if you can edit an XBlock (in Studio), you can edit that XBlock's tags. If you can edit a course (in Studio), you can edit that course's tags. Otherwise, permission denied. This is in addition to the checks in the base class which check the taxonomy side of things: which taxonomies/tags you are allowed to use when you make these edits.

If the written requirements anywhere are different than this, please point it out and I'll get them updated.

@rpenido rpenido force-pushed the rpenido/fal-3474-implement_tagging_rest_api_tag_object branch 2 times, most recently from 201871f to 5851ee2 Compare August 29, 2023 20:35
@rpenido
Copy link
Contributor Author

rpenido commented Aug 29, 2023

Hi @bradenmacdonald! I was thinking about it and would like to make sure we are on the right path.
Today, the permissions are defined in the rules.py (for openedx-learning and edx-platform. So, I created a new rule to check the object_id permission and made the calls accordingly.

I still can remove the update method from the View and create a Mixin to be used in edx-platform. The side effect will be that we will not be able to test the code here.

Also, as soon we change the object_id permission here to False, we will not be able to test the endpoint here.

Let me know what you think!
The changes are here: 5851ee2

The initial requirements are here:

@bradenmacdonald
Copy link
Contributor

bradenmacdonald commented Aug 29, 2023

I still can remove the update method from the View and create a Mixin to be used in edx-platform. The side effect will be that we will not be able to test the code here.

I was thinking the whole view should just be an independent class, because every CRUD action depends on the user's permissions. Even just reading the tags should only be allowed if the user can "read" the object that is tagged too.

So what if we just make the view so that it is designed to be used as an abstract view. It should work, but it won't be in urls.py by default. However, for tests in this repo, it can be added to urls.py so that it can be tested.

Then, in edx-platform, we can subclass the view, and give it a new URL endpoint, and implement the permissions checks for "can user read/write this content object". This view also will only work with ContentObjectTags.

Then if there are other use cases for tagging in the future, each use case can subclass the base view and implement its own permission checks on top of it.


Another way is what you are saying, where the base implementation in openedx-learning just checks the rules and applications like edx-platform that are using it just define the rules. If we want to go that approach, it makes sense, but we need some way to say "this is the rule for ContentObjectTags" and "this is the rule for ObjectTags in general" and set other rules for other subclasses of ObjectTag. I think that would work well too, and then we wouldn't need to subclass the view at all. I'm just not sure if that's how rules works. Were you able to get something like this working?

exc.exception
assert "Invalid object tag for taxonomy (1): Eukaryota Xenomorph" in str(exc.exception)

def test_tag_object_string(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

If there are no type declarations on the function definition, please add -> None so it becomes def test(self) -> None: . Specifying any type hints, including just -> None will enable type checking for the code in this function. But if there are no type annotations at all in the function declaration then the whole function will be ignored and can contain type errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for pointing out! I think we have some type errors here. I will check it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 66e5f2a

@rpenido
Copy link
Contributor Author

rpenido commented Aug 29, 2023

Another way is what you are saying, where the base implementation in openedx-learning just checks the rules and applications like edx-platform that are using it just define the rules. If we want to go that approach, it makes sense, but we need some way to say "this is the rule for ContentObjectTags" and "this is the rule for ObjectTags in general" and set other rules for other subclasses of ObjectTag.

It is more like this rule is for 'oel_tagging' (openedx-learning) and this rule is for 'content_tagging' (edx-platform).
But we are talking about the same thing here, right?
We use the app_label to define witch rule should be checked. The Django ModelViewSet with DjangoObjectPermissions and the rules package works out of the box that way.

I think that would work well too, and then we wouldn't need to subclass the view at all. I'm just not sure if that's how rules works. Were you able to get something like this working?

Yes, if "one rule" per app_label is enough for us (of course we could do any type check inside our rule function to handle anything we want). I think we should stick with the rules only because the other rest apis are using it.

And I if the requirements where made that way because openedx-learning could work as a stand alone app and they want the tagging feature there. If we leave the rule oel_tagging.change_objecttag_objectid True here in oel_tagging, this doesn't affect edx-platform at all.

@bradenmacdonald
Copy link
Contributor

One rule per app_label is fine yep. But how does "rules" know to use a different app label for content tags like tags on an XBlock? Won't it still think the app is oel_tagging because that's where the API is defined?

@rpenido
Copy link
Contributor Author

rpenido commented Aug 29, 2023

One rule per app_label is fine yep. But how does "rules" know to use a different app label for content tags like tags on an XBlock? Won't it still think the app is oel_tagging because that's where the API is defined?

The DjangoObjectPermissions uses the app that owns the Model.

https://github.com/encode/django-rest-framework/blob/40eccb0d6cdb769876dbb79724c5871b4f04163d/rest_framework/permissions.py#L280-L284

But now I understand what you are saying. We can't use the taxonomy_class model app (i.e. LanguageTaxonomy is from oel_tagging). But I'm intrigued by how it worked in the Taxonomy API (the rules work out of the box without issues); I will take a look at it tomorrow.

Thank you for your insight and input @bradenmacdonald !

@rpenido
Copy link
Contributor Author

rpenido commented Aug 30, 2023

I got it. We explicitly override the permission from oel_tagging (not content_tagging) in our rules.py of edx_platform app.

https://github.com/openedx/edx-platform/blob/98fda4110056077fbdd325f325ad8fcf76460124/openedx/features/content_tagging/rules.py#L113

Do you fell that we can go on this route @bradenmacdonald?
And is it okay to let the tag permission be enabled in openedx-learning to allow tests?

@bradenmacdonald
Copy link
Contributor

I'm not sure. But if you try it out, and it allows edx-platform to specify the permissions for content objects, other future apps to specify the permissions for tagging their own models, and openedx-tagging to test its API code, then it sounds good.

I see there is both https://github.com/openedx/openedx-learning/blob/f73adefbcf9748dafe8ebfb7e8e6590df6780bb2/openedx_tagging/core/tagging/rules.py#L71 and https://github.com/openedx/edx-platform/blob/98fda4110056077fbdd325f325ad8fcf76460124/openedx/features/content_tagging/rules.py#L113 - how do we know that the platform one always overrides the other? And what is the point of the override when the implementation of can_change_taxonomy() is the same on both sides?

@rpenido
Copy link
Contributor Author

rpenido commented Aug 30, 2023

I checked now (using breakpoints), and I'm sure that NOW the edx-platform overrides the rule.

Is it safe to say that the platform will always override because we do import openedx_tagging.core.tagging.rules as oel_tagging there (so it runs after because of the dependency)? Just want to double-check that.

And what is the point of the override when the implementation of can_change_taxonomy() is the same on both sides?

The code started differently (we had an Organization permission check) and converged when we simplified it. I didn't notice that in the Taxonomy Org PR. My bad!

@rpenido rpenido force-pushed the rpenido/fal-3474-implement_tagging_rest_api_tag_object branch from 186e6c6 to c9d4ccd Compare August 30, 2023 21:12
@bradenmacdonald
Copy link
Contributor

Is it safe to say that the platform will always override because we do import openedx_tagging.core.tagging.rules as oel_tagging there (so it runs after because of the dependency)? Just want to double-check that.

I think so, yes. But there should be some unit tests in edx-platform to check that the permissions are correct just in case.

@rpenido
Copy link
Contributor Author

rpenido commented Aug 30, 2023

Is it safe to say that the platform will always override because we do import openedx_tagging.core.tagging.rules as oel_tagging there (so it runs after because of the dependency)? Just want to double-check that.

I think so, yes. But there should be some unit tests in edx-platform to check that the permissions are correct just in case.

Yes! We have the rule tests here:
https://github.com/openedx/edx-platform/blob/98fda4110056077fbdd325f325ad8fcf76460124/openedx/features/content_tagging/tests/test_rules.py

The view tests also cover that!

So, are we good to go that way?

PS: It is better to handle the duplicate rule in the next edx-platform task related to the tagging or create a microtask for that?

@bradenmacdonald
Copy link
Contributor

You can add the cleanup to FAL-3477.

I'm not sure those tests are comprehensive. Because there was a missed requirement here: the requirements/specs for this ticket focused too much on the taxonomies and org-level permissions; those tests are right. But we also need to check if the user has permissions on the object, and I don't see any tests doing that. For example https://github.com/openedx/edx-platform/blob/98fda4110056077fbdd325f325ad8fcf76460124/openedx/features/content_tagging/tests/test_rules.py#L380-L394 is not checking the object permissions at all. We'll have to address that missed requirement in the future.

Obviously if I can't edit an XBlock in Studio, I shouldn't be able to edit that XBlock's tags regardless of what other permissions I have at the org and taxonomy level.

@rpenido
Copy link
Contributor Author

rpenido commented Aug 30, 2023

But we also need to check if the user has permissions on the object, and I don't see any tests doing that.

Yes. The rules don't cover the object permission. This could be done when we create the tag object rest api (I didn't find this task in the project)

@rpenido
Copy link
Contributor Author

rpenido commented Aug 30, 2023

Are we good for a final review here @bradenmacdonald? I think we addressed all the questions here.

The only loose end is the object permission in edx-platform, which should be fixed (the rule and the view with tests) in the task (not created) that implement the rest API for edx-platform.

Am I missing something?

Thank you!

Copy link
Contributor

@bradenmacdonald bradenmacdonald left a comment

Choose a reason for hiding this comment

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

Please make these minor changes but it looks good! I can merge once you let me know.

def update(self, request, object_id, partial=False):
"""
Update ObjectTags that belong to a given object_id and
return the list of these ObjecTags paginated.
Copy link
Contributor

Choose a reason for hiding this comment

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

The response isn't really paginated is it? I don't think we need pagination.

Also, do we have a limit on how many tags can be applied to one object? If not, I'd like to add a limit in. Something like 100 so that we can avoid any need for pagination in the object tag APIs. That can come in a separate task though, no need to do in this PR. Just let me know what the current status is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It calls the retrieve method, which is paginated.

We don't have a limit on tags per object.

I think the idea here is to return the default GET format to make the refresh on the frontend easier and save 1 extra call.

Personally, I prefer to return no data in the PUT/POST requests and let the frontend handle the logic to make another GET call to get refreshed data. We can fine-tune this when we make the frontend.

Copy link
Contributor

Choose a reason for hiding this comment

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

@rpenido We will need to have a limit on tags per object. I created openedx/modular-learning#99 but it's not ready yet as I'm waiting for the product folks to confirm what the actual limit is. When we implement that ticket we can probably remove pagination from the GET API.

I personally usually like to make the POST/PUT API return the same thing as the GET API. It saves the frontend from making an extra request.

"""
Nobody can create or modify object tags without checking the permission for the tagged object.

This rule should be defined in other apps for proper permission checking.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have some options here:

  1. return True for this app (I think it is ok to tag everything here: we are tagging str)
  2. return False and change the tests accordingly (everything will return 403 and will barely cover the implemented code in the rest_api)
  3. return False and change the tests accordingly (everything will return 403) AND duplicate the tests patching this permission (forcing True) to actually test the workflow.

In the actual context, 1 is the faster/easier path. For 2 and 3, I must fix the test_rules.py and test_views.py. Let me know what you think!

Copy link
Contributor

Choose a reason for hiding this comment

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

In edx-platform, we'll be adding a rule that defines how "content objects" are tagged.

So in the tests, can't we add a new rule just for tests that defines how "Foo objects" are tagged? And then confirms that the rule is enforced by trying to tag some Foo objects?

In other words, the tests should work in a similar way to how the platform code is going to work. I would prefer no patching and I want to see the actual functionality being tested so I don't like 2 or 3.

Please review openedx/modular-learning#98 , I created this ticket for next sprint to clearly define the missed requirements here around permissions. If you think these tests will take a while, we can just return True for now and make sure the permissions are properly tested when we implement that ticket but I'd prefer to make sure that the base functionality in openedx-learning is implemented correctly now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So in the tests, can't we add a new rule just for tests that defines how "Foo objects" are tagged? And then confirms that the rule is enforced by trying to tag some Foo objects?

Awesome idea @bradenmacdonald!

Copy link
Contributor Author

@rpenido rpenido Sep 1, 2023

Choose a reason for hiding this comment

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

@bradenmacdonald I would like to add some tests to check if we can't tag anything but "abc". Just give me a minute before merge!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Phew! It had a copy/paste error. Tested and fixed 85b320a

I think we are good to go now @bradenmacdonald!

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like a build error. No rush, just let me know when it's green and ready :)

Copy link
Contributor Author

@rpenido rpenido Sep 1, 2023

Choose a reason for hiding this comment

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

I think we are good to go now @bradenmacdonald!

Too soon.

openedx_tagging/core/tagging/rules.py:92: error: Argument 2 to "has_perm" of "PermissionsMixin" has incompatible type "str"; expected "Optional[Union[Model, AnonymousUser]]"  [arg-type]
openedx_tagging/core/tagging/rules.py:92: error: Argument 2 to "has_perm" of "AnonymousUser" has incompatible type "str"; expected "Optional[Union[Model, AnonymousUser]]"  [arg-type]

Where mypi gets these types from?

Copy link
Contributor

Choose a reason for hiding this comment

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

@rpenido According to the django docs for has_perm it seems like the second object is usually an object, but here we're passing in a string (the object ID). So I think the type check is mostly correct. BUT here we don't have objects, we have object IDs, and the API doesn't actually care how you define the permissions checks. I think defining them to use the object_id is quite reasonable. So just add a comment explaining that and put #type: ignore[arg-type] at the end of the line to suppress the warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you @bradenmacdonald! We are good now!²

@bradenmacdonald
Copy link
Contributor

@rpenido Sorry, it says it needs to be rebased before it merges.

@rpenido rpenido force-pushed the rpenido/fal-3474-implement_tagging_rest_api_tag_object branch from 416d34f to 2c0b997 Compare September 1, 2023 20:44
@rpenido
Copy link
Contributor Author

rpenido commented Sep 1, 2023

Rebased and squashed @bradenmacdonald !

@bradenmacdonald bradenmacdonald merged commit 8f39d07 into openedx:main Sep 1, 2023
@openedx-webhooks
Copy link

@rpenido 🎉 Your pull request was merged! Please take a moment to answer a two question survey so we can improve your experience in the future.

@bradenmacdonald bradenmacdonald deleted the rpenido/fal-3474-implement_tagging_rest_api_tag_object branch September 1, 2023 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FC Relates to an Axim Funded Contribution project open-source-contribution PR author is not from Axim or 2U

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

[Tagging] Implement tagging REST API: Update object tags

4 participants