Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions contentcuration/contentcuration/tests/viewsets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from contentcuration.viewsets.sync.utils import generate_publish_event as base_generate_publish_event
from contentcuration.viewsets.sync.utils import generate_update_event as base_generate_update_event
from contentcuration.viewsets.sync.utils import generate_update_descendants_event as base_generate_update_descendants_event
from contentcuration.viewsets.sync.utils import generate_publish_staging_tree_event as base_generate_publish_staging_tree_event


def generate_copy_event(*args, **kwargs):
Expand Down Expand Up @@ -66,6 +67,11 @@ def generate_publish_channel_event(channel_id):
event["rev"] = random.randint(1, 10000000)
return event

def generate_publish_staging_tree_event(channel_id):
event = base_generate_publish_staging_tree_event(channel_id)
event["rev"] = random.randint(1, 10000000)
return event


class SyncTestMixin(object):
celery_task_always_eager = None
Expand Down
67 changes: 67 additions & 0 deletions contentcuration/contentcuration/tests/viewsets/test_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,26 @@
from contentcuration.tests.viewsets.base import generate_delete_event
from contentcuration.tests.viewsets.base import generate_deploy_channel_event
from contentcuration.tests.viewsets.base import generate_publish_channel_event
from contentcuration.tests.viewsets.base import generate_publish_staging_tree_event
from contentcuration.tests.viewsets.base import generate_sync_channel_event
from contentcuration.tests.viewsets.base import generate_update_event
from contentcuration.tests.viewsets.base import SyncTestMixin
from contentcuration.viewsets.channel import _unpublished_changes_query
from contentcuration.viewsets.sync.constants import CHANNEL
from mock import patch


class SyncTestCase(SyncTestMixin, StudioAPITestCase):
@classmethod
def setUpClass(cls):
super(SyncTestCase, cls).setUpClass()
cls.patch_copy_db = patch('contentcuration.utils.publish.save_export_database')
cls.mock_save_export = cls.patch_copy_db.start()

@classmethod
def tearDownClass(cls):
super(SyncTestCase, cls).tearDownClass()
cls.patch_copy_db.stop()

@property
def channel_metadata(self):
Expand Down Expand Up @@ -391,6 +403,61 @@ def test_publish_does_not_make_publishable(self):

self.assertEqual(_unpublished_changes_query(channel).count(), 0)

def test_publish_staging_tree(self):
channel = testdata.channel()
user = testdata.user()
channel.editors.add(user)
self.client.force_authenticate(
user
) # This will skip all authentication checks

channel.staging_tree = testdata.tree()
node = testdata.node({
'kind_id': 'video', 'title': 'title', 'children': []})
node.complete = True
node.parent = channel.staging_tree
node.save()
channel.staging_tree.save()
channel.save()
self.assertEqual(channel.staging_tree.published, False)

response = self.sync_changes(
[
generate_publish_staging_tree_event(channel.id)
]
)

self.assertEqual(response.status_code, 200)
modified_channel = models.Channel.objects.get(id=channel.id)
self.assertEqual(modified_channel.staging_tree.published, True)

def test_publish_staging_tree_incomplete(self):
channel = testdata.channel()
user = testdata.user()
channel.editors.add(user)
self.client.force_authenticate(
user
) # This will skip all authentication checks

channel.staging_tree = cc.ContentNode(
kind_id=content_kinds.TOPIC, title="test", node_id="aaa"
)
channel.staging_tree.save()
channel.save()
self.assertEqual(channel.staging_tree.published, False)

response = self.sync_changes(
[
generate_publish_staging_tree_event(channel.id)
]
)

self.assertEqual(response.status_code, 200)
self.assertTrue(
"Channel is not ready to be published" in response.json()["errors"][0]["errors"][0])
modified_channel = models.Channel.objects.get(id=channel.id)
self.assertEqual(modified_channel.staging_tree.published, False)


class CRUDTestCase(StudioAPITestCase):
@property
Expand Down
49 changes: 49 additions & 0 deletions contentcuration/contentcuration/viewsets/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,55 @@ def publish(self, pk, version_notes="", language=None):
], applied=True, unpublishable=True)
raise

def publish_staging_tree_from_changes(self, changes):
errors = []
for publish in changes:
try:
self.publish_staging_tree(publish["key"])
except Exception as e:
log_sync_exception(e, user=self.request.user, change=publish)
publish["errors"] = [str(e)]
errors.append(publish)
return errors

def publish_staging_tree(self, pk):
logging.debug("Entering the publish staging channel endpoint")

channel = self.get_edit_queryset().get(pk=pk)

if channel.deleted:
raise ValidationError("Cannot publish a deleted channel")
elif channel.staging_tree.publishing:
raise ValidationError("Channel staging tree is already publishing")

channel.staging_tree.publishing = True
channel.staging_tree.save()

with create_change_tracker(pk, CHANNEL, channel.id, self.request.user,
"export-channel-staging-tree") as progress_tracker:
try:
channel = publish_channel(
self.request.user.pk,
channel.id,
progress_tracker=progress_tracker,
use_staging_tree=True,
)
Change.create_changes([
generate_update_event(
channel.id, CHANNEL, {
"primary_token": channel.get_human_token().token,
}, channel_id=channel.id
),
], applied=True)
except ChannelIncompleteError:
channel.staging_tree.publishing = False
channel.staging_tree.save()
raise ValidationError("Channel is not ready to be published")
except Exception:
channel.staging_tree.publishing = False
channel.staging_tree.save()
raise

def sync_from_changes(self, changes):
errors = []
for sync in changes:
Expand Down
2 changes: 2 additions & 0 deletions contentcuration/contentcuration/viewsets/sync/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from contentcuration.viewsets.sync.constants import DELETED
from contentcuration.viewsets.sync.constants import DEPLOYED
from contentcuration.viewsets.sync.constants import UPDATED_DESCENDANTS
from contentcuration.viewsets.sync.constants import STAGING_TREE_PUBLISHED
from contentcuration.viewsets.sync.constants import EDITOR_M2M
from contentcuration.viewsets.sync.constants import FILE
from contentcuration.viewsets.sync.constants import INVITATION
Expand Down Expand Up @@ -96,6 +97,7 @@ def get_change_type(obj):
SYNCED: "sync_from_changes",
DEPLOYED: "deploy_from_changes",
UPDATED_DESCENDANTS: "update_descendants_from_changes",
STAGING_TREE_PUBLISHED: "publish_staging_tree_from_changes"
}


Expand Down
2 changes: 2 additions & 0 deletions contentcuration/contentcuration/viewsets/sync/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
SYNCED = 7
DEPLOYED = 8
UPDATED_DESCENDANTS = 9
STAGING_TREE_PUBLISHED = 10


ALL_CHANGES = set([
Expand All @@ -20,6 +21,7 @@
SYNCED,
DEPLOYED,
UPDATED_DESCENDANTS,
STAGING_TREE_PUBLISHED,
])

# Client-side table constants
Expand Down
7 changes: 7 additions & 0 deletions contentcuration/contentcuration/viewsets/sync/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from contentcuration.viewsets.sync.constants import PUBLISHED
from contentcuration.viewsets.sync.constants import UPDATED
from contentcuration.viewsets.sync.constants import UPDATED_DESCENDANTS
from contentcuration.viewsets.sync.constants import STAGING_TREE_PUBLISHED


def validate_table(table):
Expand Down Expand Up @@ -88,6 +89,12 @@ def generate_update_descendants_event(key, mods, channel_id=None, user_id=None):
event["mods"] = mods
return event

def generate_publish_staging_tree_event(key, version_notes="", language=None):
event = _generate_event(key, CHANNEL, STAGING_TREE_PUBLISHED, key, None)
event["version_notes"] = version_notes
event["language"] = language
return event

def log_sync_exception(e, user=None, change=None, changes=None):
# Capture exception and report, but allow sync
# to complete properly.
Expand Down