M220P Ch 2 : Update Comments appears to pass test, but fails validation

Please advise Update Comments appears to pass test, but fails validation.

Thanks !

image

image

I’m facing the same challenge
The issue comes from

  1. API - Updates a user comment. Validates the user is logged in by ensuring a valid JWT is provided

This API is actually a PUT method as per the code but using the same function
update_comment(comment_id, user_email, text, date) - in db.py

Don’t understand why this function works for one and don’t work for another.

Please let us know if the code is correct or need some more config.

Hi !

Please review code below and advise.

def update_comment(comment_id, user_email, text, date):

“”"

Updates the comment in the comment collection. Queries for the comment

based by both comment _id field as well as the email field to doubly ensure

the user has permission to edit this comment.

“”"

: Create/Update Comments

use the user_email and comment_id to select the proper comment

then update the “text” and “date” of the selected comment

response = db.comments.update_one(

{"_id": comment_id, “email”: user_email},

{"$set": {“text”: text, “date”: date}}

)

return response

def delete_comment(comment_id, user_email):

“”"

Given a user’s email and a comment ID, deletes a comment from the comments

collection

“”"

“”"

Ticket: Delete Comments

Match the comment_id and user_email with the correct fields, to make sure

this user has permission to delete this comment, and then delete it.

“”"

: Delete Comments

use the user_email and comment_id to delete the proper comment

response = db.comments.delete_one({"_id": comment_id, “email”: user_email})

return response

“”"

Thanks !

Ajai

Hi !

Thanks it worked out, my bad.

Please can you also advise on the migration code.

from pymongo import MongoClient, UpdateOne

from pymongo.errors import InvalidOperation

from bson import ObjectId

import dateutil.parser as parser

“”"

Ticket: Migration

Update all the documents in the movies collection, such that the “lastupdated”

field is stored as an ISODate() rather than a string.

The parser.parse() method can transform date strings into ISODate objects for

us. We just need to make sure the correct operations are sent to MongoDB!

“”"

ensure you update your host information below!

host = ""mongodb+srv://m220student:m220password@mflix-sjx4f.mongodb.net/test?retryWrites=true&w=majority

mflix = MongoClient(host)[“mflix”]

TODO: Create the proper predicate and projection

add a predicate that checks that the “lastupdated” field exists, and then

checks that its type is a string

a projection is not required, but may help reduce the amount of data sent

over the wire!

predicate = {“lastupdated”: {"$exists": True} , “lastupdated”: {"$type": “string”}}

projection = {“lastupdated”: 1, “_id”: 1}

cursor = mflix.movies.find(predicate, projection)

this will transform the “lastupdated” field to an ISODate() from a string

movies_to_migrate =

for doc in cursor:

doc_id = doc.get(’_id’)

lastupdated = doc.get(‘lastupdated’, None)

movies_to_migrate.append(

{

“doc_id”: ObjectId(doc_id),

“lastupdated”: parser.parse(lastupdated)

}

)

print(f"{len(movies_to_migrate)} documents to migrate")

try:

TODO: Complete the UpdateOne statement below

build the UpdateOne so it updates the “lastupdated” field to contain

the new ISODate() type

bulk_updates = [UpdateOne(

{"_id": movie.get(“doc_id”)},

{"$set": {“lastupdated”: movie.get(“lastupdated”)}}

) for movie in movies_to_migrate]

here’s where the bulk operation is sent to MongoDB

bulk_results = mflix.movies.bulk_write(bulk_updates)

print(f"{bulk_results.modified_count} documents updated")

except InvalidOperation:

print(“no updates necessary”)

except Exception as e:

print(str(e))

Make sure you are using the right database name.

Kanika

One more please. The GET COMMENTS code is pasted below , please advise.

def get_movie(id):
“”"
Given a movie ID, return a movie with that ID, with the comments for that
movie embedded in the movie document. The comments are joined from the
comments collection using expressive $lookup.
“”"
try:

    """
    Ticket: Get Comments
    Please implement a $lookup stage in this pipeline to find all the
    comments for the given movie. The movie_id in the `comments` collection
    can be used to refer to the _id from the `movies` collection.
    Embed the joined comments in a new field called "comments".
    """

    # : Get Comments
    # implement the required pipeline
    pipeline = [
        {
            "$match": {
                "_id": ObjectId(id)
            }
        },
        {
            "$lookup": {
                "from": 'comments',
                "let": { 'id': '$_id' },
                "pipeline": [
                    { '$match':
                        { '$expr': { '$eq': [ '$movie_id', '$$id' ] } }
                    }
                ],
                "as": 'comments'
            }
        }
    ]

    movie = db.movies.aggregate(pipeline).next()
    movie["comments"] = sorted(
        movie.get("comments", []),
        key=lambda c: c.get("date"),
        reverse=True
    )
    return movie

pipeline = [
{
“$match”: {
“_id”: ObjectId(id)
}
},
{
lookup': { 'from': 'comments', 'let': { 'id': '_id’
},
‘pipeline’: [
{
‘$match’: {
‘$expr’: {
‘$eq’: [
‘$movie_id’, ‘$$id’
]
}
}
},
{
‘$sort’ : {‘date’: -1}
}
],
‘as’: ‘comments’
}
}
]

hi
i did use ObjectId() function but im not able to get validation key.

response = db.comments.delete_one(
{"_id": ObjectId(comment_id), “email”: user_email}
)

where i did wrong?

i have the same issue in add and update comment but i have been using Objecid func into these tickets.
comment_doc = {“name”:user.name,“email”:user.email,“movie_id”:ObjectId(movie_id),“text”:comment,“date”:date}

response = db.comments.update_one({"_id": ObjectId(comment_id),“email”:user_email}, {"$set": {“text”: text,“date”: date}})

the tests is not passing also. is there any problem with api code??

What is the console output when running unit tests?

Kanika

Delete comment test
==================================================================================================== test session starts =====================================================================================================
platform linux – Python 3.7.3, pytest-3.10.1, py-1.7.0, pluggy-0.8.0
rootdir: /home/reza/Documents/Python Apps/Learning/mflix-pyhton, inifile:
plugins: flask-0.10.0
collected 43 items / 40 deselected

tests/test_delete_comments.py FFF [100%]

========================================================================================================== FAILURES ==========================================================================================================
___________________________________________________________________________________________ test_add_comment_should_be_implemented ___________________________________________________________________________________________

client = <FlaskClient <Flask ‘mflix.factory’>>

@pytest.mark.delete_comments
def test_add_comment_should_be_implemented(client):
    # we need to add a comment
    # this test serves to do that
  result = add_comment(movie_id, user, comment['text'], now)

tests/test_delete_comments.py:42:


mflix/db.py:350: in add_comment
return db.comments.insert_one(comment_doc)
/home/reza/.local/lib/python3.7/site-packages/pymongo/collection.py:693: in insert_one
session=session),
/home/reza/.local/lib/python3.7/site-packages/pymongo/collection.py:607: in _insert
bypass_doc_val, session)
/home/reza/.local/lib/python3.7/site-packages/pymongo/collection.py:595: in _insert_one
acknowledged, _insert_command, session)
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1247: in _retryable_write
with self._tmp_session(session) as s:
/usr/lib/python3.7/contextlib.py:112: in enter
return next(self.gen)
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1576: in _tmp_session
s = self._ensure_session(session)
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1563: in _ensure_session
return self.__start_session(True, causal_consistency=False)
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1516: in __start_session
server_session = self._get_server_session()
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1549: in _get_server_session
return self._topology.get_server_session()
/home/reza/.local/lib/python3.7/site-packages/pymongo/topology.py:432: in get_server_session
None)


self = <pymongo.topology.Topology object at 0x7f1eceff21d0>, selector = <function readable_server_selector at 0x7f1ecf5098c8>, timeout = 30, address = None

def _select_servers_loop(self, selector, timeout, address):
    """select_servers() guts. Hold the lock when calling this."""
    now = _time()
    end_time = now + timeout
    server_descriptions = self._description.apply_selector(
        selector, address)

    while not server_descriptions:
        # No suitable servers.
        if timeout == 0 or now > end_time:
            raise ServerSelectionTimeoutError(
              self._error_message(selector))

E pymongo.errors.ServerSelectionTimeoutError: your_testing_db_uri (can be the same as atlas:27017: [Errno -2] Name or service not known, or a local mongodb database):27017: [Errno -2] Name or service not known

/home/reza/.local/lib/python3.7/site-packages/pymongo/topology.py:199: ServerSelectionTimeoutError
___________________________________________________________________________________ test_should_not_delete_comment_if_email_does_not_match ___________________________________________________________________________________

client = <FlaskClient <Flask ‘mflix.factory’>>

@pytest.mark.delete_comments
def test_should_not_delete_comment_if_email_does_not_match(client):
  result = delete_comment(comment['id'], "fakeemail@email.com")

tests/test_delete_comments.py:51:


mflix/db.py:383: in delete_comment
{"_id": ObjectId(comment_id), “email”: user_email}
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:125: in init
self.__validate(oid)
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:221: in __validate
_raise_invalid_id(oid)


oid = ‘’

def _raise_invalid_id(oid):
    raise InvalidId(
        "%r is not a valid ObjectId, it must be a 12-byte input"
      " or a 24-character hex string" % oid)

E bson.errors.InvalidId: ‘’ is not a valid ObjectId, it must be a 12-byte input or a 24-character hex string

/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:67: InvalidId
____________________________________________________________________________________ test_delete_comment_should_delete_if_email_is_owner _____________________________________________________________________________________

client = <FlaskClient <Flask ‘mflix.factory’>>

@pytest.mark.delete_comments
def test_delete_comment_should_delete_if_email_is_owner(client):
  result = delete_comment(comment['id'], test_user['email'])

tests/test_delete_comments.py:57:


mflix/db.py:383: in delete_comment
{"_id": ObjectId(comment_id), “email”: user_email}
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:125: in init
self.__validate(oid)
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:221: in __validate
_raise_invalid_id(oid)


oid = ‘’

def _raise_invalid_id(oid):
    raise InvalidId(
        "%r is not a valid ObjectId, it must be a 12-byte input"
      " or a 24-character hex string" % oid)

E bson.errors.InvalidId: ‘’ is not a valid ObjectId, it must be a 12-byte input or a 24-character hex string

/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:67: InvalidId
====================================================================================================== warnings summary ======================================================================================================
tests/test_delete_comments.py::test_add_comment_should_be_implemented
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:109: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:61: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:78: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)

tests/test_delete_comments.py::test_should_not_delete_comment_if_email_does_not_match
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:109: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:61: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:78: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)

tests/test_delete_comments.py::test_delete_comment_should_delete_if_email_is_owner
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:109: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:61: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:78: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)

– Docs: https://docs.pytest.org/en/latest/warnings.html
==================================================================================== 3 failed, 40 deselected, 9 warnings in 30.37 seconds ====================================================================================

Create and update comment test:
==================================================================================================== test session starts =====================================================================================================
platform linux – Python 3.7.3, pytest-3.10.1, py-1.7.0, pluggy-0.8.0
rootdir: /home/reza/Documents/Python Apps/Learning/mflix-pyhton, inifile:
plugins: flask-0.10.0
collected 43 items / 39 deselected

tests/test_create_update_comments.py FFFF [100%]

========================================================================================================== FAILURES ==========================================================================================================
______________________________________________________________________________________________________ test_add_comment ______________________________________________________________________________________________________

client = <FlaskClient <Flask ‘mflix.factory’>>

@pytest.mark.create_update_comments
def test_add_comment(client):
  result = add_comment(movie_id, user, comment['text'], now)

tests/test_create_update_comments.py:35:


mflix/db.py:350: in add_comment
return db.comments.insert_one(comment_doc)
/home/reza/.local/lib/python3.7/site-packages/pymongo/collection.py:693: in insert_one
session=session),
/home/reza/.local/lib/python3.7/site-packages/pymongo/collection.py:607: in _insert
bypass_doc_val, session)
/home/reza/.local/lib/python3.7/site-packages/pymongo/collection.py:595: in _insert_one
acknowledged, _insert_command, session)
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1247: in _retryable_write
with self._tmp_session(session) as s:
/usr/lib/python3.7/contextlib.py:112: in enter
return next(self.gen)
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1576: in _tmp_session
s = self._ensure_session(session)
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1563: in _ensure_session
return self.__start_session(True, causal_consistency=False)
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1516: in __start_session
server_session = self._get_server_session()
/home/reza/.local/lib/python3.7/site-packages/pymongo/mongo_client.py:1549: in _get_server_session
return self._topology.get_server_session()
/home/reza/.local/lib/python3.7/site-packages/pymongo/topology.py:432: in get_server_session
None)


self = <pymongo.topology.Topology object at 0x7f05a55cd160>, selector = <function readable_server_selector at 0x7f05a5ae58c8>, timeout = 30, address = None

def _select_servers_loop(self, selector, timeout, address):
    """select_servers() guts. Hold the lock when calling this."""
    now = _time()
    end_time = now + timeout
    server_descriptions = self._description.apply_selector(
        selector, address)

    while not server_descriptions:
        # No suitable servers.
        if timeout == 0 or now > end_time:
            raise ServerSelectionTimeoutError(
              self._error_message(selector))

E pymongo.errors.ServerSelectionTimeoutError: your_testing_db_uri (can be the same as atlas:27017: [Errno -2] Name or service not known, or a local mongodb database):27017: [Errno -2] Name or service not known

/home/reza/.local/lib/python3.7/site-packages/pymongo/topology.py:199: ServerSelectionTimeoutError
____________________________________________________________________________________________________ test_update_comment _____________________________________________________________________________________________________

client = <FlaskClient <Flask ‘mflix.factory’>>

@pytest.mark.create_update_comments
def test_update_comment(client):
  result = update_comment(comment['id'], user.email, 'foo foo foo', now)

tests/test_create_update_comments.py:48:


mflix/db.py:362: in update_comment
response = db.comments.update_one({"_id": ObjectId(comment_id),“email”:user_email}, {"$set": {“text”: text,“date”: date}})
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:125: in init
self.__validate(oid)
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:221: in __validate
_raise_invalid_id(oid)


oid = ‘’

def _raise_invalid_id(oid):
    raise InvalidId(
        "%r is not a valid ObjectId, it must be a 12-byte input"
      " or a 24-character hex string" % oid)

E bson.errors.InvalidId: ‘’ is not a valid ObjectId, it must be a 12-byte input or a 24-character hex string

/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:67: InvalidId
_________________________________________________________________________________________ test_do_not_update_comment_if_is_not_owner _________________________________________________________________________________________

client = <FlaskClient <Flask ‘mflix.factory’>>

@pytest.mark.create_update_comments
def test_do_not_update_comment_if_is_not_owner(client):
  result = update_comment(comment['id'], n_user.email, 'blah', now)

tests/test_create_update_comments.py:58:


mflix/db.py:362: in update_comment
response = db.comments.update_one({"_id": ObjectId(comment_id),“email”:user_email}, {"$set": {“text”: text,“date”: date}})
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:125: in init
self.__validate(oid)
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:221: in __validate
_raise_invalid_id(oid)


oid = ‘’

def _raise_invalid_id(oid):
    raise InvalidId(
        "%r is not a valid ObjectId, it must be a 12-byte input"
      " or a 24-character hex string" % oid)

E bson.errors.InvalidId: ‘’ is not a valid ObjectId, it must be a 12-byte input or a 24-character hex string

/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:67: InvalidId
____________________________________________________________________________________________________ test_delete_is_given ____________________________________________________________________________________________________

client = <FlaskClient <Flask ‘mflix.factory’>>

@pytest.mark.create_update_comments
def test_delete_is_given(client):
    # we are mainly running this for cleanup to delete the comment
  result = delete_comment(comment['id'], test_user['email'])

tests/test_create_update_comments.py:65:


mflix/db.py:383: in delete_comment
{"_id": ObjectId(comment_id), “email”: user_email}
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:125: in init
self.__validate(oid)
/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:221: in __validate
_raise_invalid_id(oid)


oid = ‘’

def _raise_invalid_id(oid):
    raise InvalidId(
        "%r is not a valid ObjectId, it must be a 12-byte input"
      " or a 24-character hex string" % oid)

E bson.errors.InvalidId: ‘’ is not a valid ObjectId, it must be a 12-byte input or a 24-character hex string

/home/reza/.local/lib/python3.7/site-packages/bson/objectid.py:67: InvalidId
====================================================================================================== warnings summary ======================================================================================================
tests/test_create_update_comments.py::test_add_comment
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:109: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:61: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:78: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)

tests/test_create_update_comments.py::test_update_comment
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:109: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:61: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:78: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)

tests/test_create_update_comments.py::test_do_not_update_comment_if_is_not_owner
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:109: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:61: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:78: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)

tests/test_create_update_comments.py::test_delete_is_given
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:109: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:61: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)
/home/reza/.local/lib/python3.7/site-packages/pytest_flask/plugin.py:78: RemovedInPytest4Warning: getfuncargvalue is deprecated, use getfixturevalue
app = request.getfuncargvalue(‘app’)

– Docs: https://docs.pytest.org/en/latest/warnings.html
=================================================================================== 4 failed, 39 deselected, 12 warnings in 30.43 seconds ====================================================================================

Hi @Mohammad_Reza_47396,

Are you running the tests from mflix-python directory? If not, try from that directory and let me know.

Kanika

Also, this means in your configuration file i.e. .ini file, you did not update the URI with your cluster for the testing environment.

Kanika