[BD-14] Migrate all environments to use database-backed organizations (#25153)

* Install `organizations` app into LMS and Studio non-optionally.
* Add toggle `ORGANIZATIONS_AUTOCREATE` to Studio.
* Remove the `FEATURES["ORGANIZATIONS_APP"]` toggle.
* Use the new `organizations.api.ensure_organization` function to
  either validate or get-or-create organizations, depending
  on the value of `ORGANIZATIONS_AUTOCREATE`,
  when creating course runs and V2 content libraries.
  We'll soon use it for V1 content libraries as well.
* Remove the `util.organizations_helpers` wrapper layer
  that had to exist because `organizations` was an optional app.
* Add `.get_library_keys()` method to the Split modulestore.
* Add Studio management command for backfilling organizations tables
  (`backfill_orgs_and_org_courses`).

For full details, see
https://github.com/edx/edx-organizations/blob/master/docs/decisions/0001-phase-in-db-backed-organizations-to-all.rst

TNL-7646
This commit is contained in:
Kyle McCormick
2020-12-02 13:58:40 -05:00
committed by GitHub
parent f096f5d685
commit 4dda73d797
30 changed files with 475 additions and 291 deletions

View File

@@ -7,15 +7,20 @@ import datetime
import ddt
import six
from django.test import override_settings
from django.test.client import RequestFactory
from django.urls import reverse
from mock import patch
from opaque_keys.edx.keys import CourseKey
from organizations.api import (
add_organization,
get_organization_by_short_name,
get_course_organizations
)
from organizations.exceptions import InvalidOrganizationException
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient, parse_json
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.organizations_helpers import add_organization, get_course_organizations
from xmodule.course_module import CourseFields
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
@@ -66,7 +71,6 @@ class TestCourseListing(ModuleStoreTestCase):
self.client.logout()
ModuleStoreTestCase.tearDown(self)
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
def test_rerun(self):
"""
Just testing the functionality the view handler adds over the tasks tested in test_clone_course
@@ -114,13 +118,15 @@ class TestCourseListing(ModuleStoreTestCase):
course = self.store.get_course(new_course_key)
self.assertTrue(course.cert_html_view_enabled)
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': False})
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_course_creation_without_org_app_enabled(self, store):
def test_course_creation_for_unknown_organization_relaxed(self, store):
"""
Tests course creation workflow should not create course to org
link if organizations_app is not enabled.
Tests that when ORGANIZATIONS_AUTOCREATE is True,
creating a course-run with an unknown org slug will create an organization
and organization-course linkage in the system.
"""
with self.assertRaises(InvalidOrganizationException):
get_organization_by_short_name("orgX")
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': 'orgX',
@@ -129,17 +135,19 @@ class TestCourseListing(ModuleStoreTestCase):
'run': '2015_T2'
})
self.assertEqual(response.status_code, 200)
self.assertIsNotNone(get_organization_by_short_name("orgX"))
data = parse_json(response)
new_course_key = CourseKey.from_string(data['course_key'])
course_orgs = get_course_organizations(new_course_key)
self.assertEqual(course_orgs, [])
self.assertEqual(len(course_orgs), 1)
self.assertEqual(course_orgs[0]['short_name'], 'orgX')
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_course_creation_with_org_not_in_system(self, store):
@override_settings(ORGANIZATIONS_AUTOCREATE=False)
def test_course_creation_for_unknown_organization_strict(self, store):
"""
Tests course creation workflow when course organization does not exist
in system.
Tests that when ORGANIZATIONS_AUTOCREATE is False,
creating a course-run with an unknown org slug will raise a validation error.
"""
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
@@ -149,12 +157,13 @@ class TestCourseListing(ModuleStoreTestCase):
'run': '2015_T2'
})
self.assertEqual(response.status_code, 400)
with self.assertRaises(InvalidOrganizationException):
get_organization_by_short_name("orgX")
data = parse_json(response)
self.assertIn(u'Organization you selected does not exist in the system', data['error'])
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_course_creation_with_org_in_system(self, store):
@ddt.data(True, False)
def test_course_creation_for_known_organization(self, organizations_autocreate):
"""
Tests course creation workflow when course organization exist in system.
"""
@@ -163,7 +172,7 @@ class TestCourseListing(ModuleStoreTestCase):
'short_name': 'orgX',
'description': 'Testing Organization Description',
})
with modulestore().default_store(store):
with override_settings(ORGANIZATIONS_AUTOCREATE=organizations_autocreate):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': 'orgX',
'number': 'CS101',

View File

@@ -127,7 +127,7 @@ class RerunCourseTaskTestCase(CourseTestCase):
old_course_id = str(old_course_key)
new_course_id = str(new_course_key)
organization = OrganizationFactory()
organization = OrganizationFactory(short_name=old_course_key.org)
OrganizationCourse.objects.create(course_id=old_course_id, organization=organization)
restricted_course = RestrictedCourse.objects.create(course_key=self.course.id)