Repository: oppia/oppia

Issue:
TODO: Story, Topic, TopicSummary models should have url_fragment as a required field
Story and Topic models should have url_fragment as a required field once all models on production and test server have url_fragment populated for existing models.


Discussion:

Resolving patch:
diff --git a/core/storage/story/gae_models.py b/core/storage/story/gae_models.py
--- a/core/storage/story/gae_models.py
+++ b/core/storage/story/gae_models.py
@@ -71,8 +71,7 @@ class StoryModel(base_models.VersionedModel):
     # The topic id to which the story belongs.
     corresponding_topic_id = ndb.StringProperty(indexed=True, required=True)
     # The url fragment for the story.
-    # TODO(#10140): Change url_fragment to a required field.
-    url_fragment = ndb.StringProperty(indexed=True)
+    url_fragment = ndb.StringProperty(required=True, indexed=True)
 
     @staticmethod
     def get_deletion_policy():
@@ -217,7 +216,7 @@ class StorySummaryModel(base_models.BaseModel):
     thumbnail_bg_color = ndb.StringProperty(indexed=True)
     version = ndb.IntegerProperty(required=True)
     # The url fragment for the story.
-    url_fragment = ndb.StringProperty(indexed=True)
+    url_fragment = ndb.StringProperty(required=True, indexed=True)
 
     @staticmethod
     def get_deletion_policy():
diff --git a/core/storage/topic/gae_models.py b/core/storage/topic/gae_models.py
--- a/core/storage/topic/gae_models.py
+++ b/core/storage/topic/gae_models.py
@@ -85,8 +85,7 @@ class TopicModel(base_models.VersionedModel):
     # The ISO 639-1 code for the language this topic is written in.
     language_code = ndb.StringProperty(required=True, indexed=True)
     # The url fragment of the topic.
-    # TODO(#10140): Topic url_fragment should be a required field.
-    url_fragment = ndb.StringProperty(indexed=True)
+    url_fragment = ndb.StringProperty(required=True, indexed=True)
 
     @staticmethod
     def get_deletion_policy():
@@ -242,8 +241,7 @@ class TopicSummaryModel(base_models.BaseModel):
     # The description of the topic.
     description = ndb.TextProperty(indexed=False)
     # The url fragment of the topic.
-    # TODO(#10140): TopicSummary url_fragment should be a required field.
-    url_fragment = ndb.StringProperty(indexed=True)
+    url_fragment = ndb.StringProperty(required=True, indexed=True)
 
     # Time when the topic model was last updated (not to be
     # confused with last_updated, which is the time when the


Test patch:
diff --git a/core/controllers/topic_editor_test.py b/core/controllers/topic_editor_test.py
--- a/core/controllers/topic_editor_test.py
+++ b/core/controllers/topic_editor_test.py
@@ -181,7 +181,8 @@ def test_story_creation(self):
             'title': 'Story title',
             'description': 'Story Description',
             'filename': 'test_svg.svg',
-            'thumbnailBgColor': '#F8BF74'
+            'thumbnailBgColor': '#F8BF74',
+            'story_url_fragment': 'story-frag-one'
         }
 
         with python_utils.open_file(
@@ -207,7 +208,8 @@ def test_story_creation_fails_with_invalid_image(self):
             'title': 'Story title',
             'description': 'Story Description',
             'filename': 'cafe.flac',
-            'thumbnailBgColor': '#F8BF74'
+            'thumbnailBgColor': '#F8BF74',
+            'story_url_fragment': 'story-frag-two'
         }
 
         with python_utils.open_file(
diff --git a/core/domain/story_jobs_one_off_test.py b/core/domain/story_jobs_one_off_test.py
--- a/core/domain/story_jobs_one_off_test.py
+++ b/core/domain/story_jobs_one_off_test.py
@@ -347,7 +347,8 @@ def test_migration_job_converts_old_story(self):
                 feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION),
             notes='Story notes',
             corresponding_topic_id=self.TOPIC_ID,
-            story_contents=story_contents
+            story_contents=story_contents,
+            url_fragment='story-frag-one'
         )
         commit_message = (
             'New story created with title \'Story title\'.')
@@ -467,7 +468,8 @@ def test_job_finds_stories_with_math_rich_text_components(self):
                 feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION),
             notes='Story notes',
             corresponding_topic_id='topic_id1',
-            story_contents=story_contents1
+            story_contents=story_contents1,
+            url_fragment='story-frag-two'
         )
         commit_message = (
             'New story created with title \'Story title1\'.')
@@ -504,7 +506,8 @@ def test_job_finds_stories_with_math_rich_text_components(self):
                 feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION),
             notes='Story notes',
             corresponding_topic_id='topic_id1',
-            story_contents=story_contents2
+            story_contents=story_contents2,
+            url_fragment='story-frag-three'
         )
         commit_message = (
             'New story created with title \'Story title2\'.')
@@ -569,7 +572,8 @@ def test_job_when_stories_do_not_have_math_rich_text(self):
                 feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION),
             notes='Story notes',
             corresponding_topic_id='topic_id1',
-            story_contents=story_contents1
+            story_contents=story_contents1,
+            url_fragment='story-frag-four'
         )
         commit_message = (
             'New story created with title \'Story title1\'.')
@@ -644,7 +648,8 @@ def test_job_when_subtopics_have_math_rich_text_with_svgs(self):
                 feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION),
             notes='Story notes',
             corresponding_topic_id='topic_id1',
-            story_contents=story_contents1
+            story_contents=story_contents1,
+            url_fragment='story-frag-five'
         )
         commit_message = (
             'New story created with title \'Story title1\'.')
@@ -681,7 +686,8 @@ def test_job_when_subtopics_have_math_rich_text_with_svgs(self):
                 feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION),
             notes='Story notes',
             corresponding_topic_id='topic_id1',
-            story_contents=story_contents2
+            story_contents=story_contents2,
+            url_fragment='story-frag-six'
         )
         commit_message = (
             'New story created with title \'Story title2\'.')
diff --git a/core/domain/topic_jobs_one_off_test.py b/core/domain/topic_jobs_one_off_test.py
--- a/core/domain/topic_jobs_one_off_test.py
+++ b/core/domain/topic_jobs_one_off_test.py
@@ -395,6 +395,7 @@ def test_job_converts_old_topic_summary(self):
             id=self.TOPIC_ID,
             name='Topic name',
             abbreviated_name='Topic',
+            url_fragment='topic-frag',
             thumbnail_bg_color='#C6DCDA',
             thumbnail_filename='topic.svg',
             canonical_name='topic name',
diff --git a/core/storage/story/gae_models_test.py b/core/storage/story/gae_models_test.py
--- a/core/storage/story/gae_models_test.py
+++ b/core/storage/story/gae_models_test.py
@@ -154,7 +154,8 @@ def test_story_summary_model(self):
             node_titles=['Chapter 1'],
             thumbnail_filename='image.svg',
             thumbnail_bg_color='#F8BF74',
-            version=1)
+            version=1,
+            url_fragment='story-summary-frag')
         story_summary_model.put()
         story_summary_by_id = story_models.StorySummaryModel.get_by_id('id')
 
@@ -165,3 +166,5 @@ def test_story_summary_model(self):
         self.assertEqual(story_summary_by_id.thumbnail_bg_color, '#F8BF74')
         self.assertEqual(story_summary_by_id.thumbnail_filename, 'image.svg')
         self.assertEqual(story_summary_by_id.version, 1)
+        self.assertEqual(
+            story_summary_by_id.url_fragment, 'story-summary-frag')
diff --git a/core/tests/test_utils.py b/core/tests/test_utils.py
--- a/core/tests/test_utils.py
+++ b/core/tests/test_utils.py
@@ -1608,7 +1608,8 @@ def save_new_story_with_story_contents_schema_v1(
             self, story_id, thumbnail_filename, thumbnail_bg_color,
             owner_id, title, description,
             notes, corresponding_topic_id,
-            language_code=constants.DEFAULT_LANGUAGE_CODE):
+            language_code=constants.DEFAULT_LANGUAGE_CODE,
+            url_fragment='story-frag'):
         """Saves a new story with a default version 1 story contents
         data dictionary.
 
@@ -1635,6 +1636,7 @@ def save_new_story_with_story_contents_schema_v1(
                 belongs.
             language_code: str. The ISO 639-1 code for the language this
                 story is written in.
+            url_fragment: str. The URL fragment for the story.
         """
         story_model = story_models.StoryModel(
             id=story_id,
@@ -1646,7 +1648,8 @@ def save_new_story_with_story_contents_schema_v1(
             story_contents_schema_version=1,
             notes=notes,
             corresponding_topic_id=corresponding_topic_id,
-            story_contents=self.VERSION_1_STORY_CONTENTS_DICT
+            story_contents=self.VERSION_1_STORY_CONTENTS_DICT,
+            url_fragment=url_fragment
         )
         commit_message = (
             'New story created with title \'%s\'.' % title)