Stuck With How To Add A User in user-management

This is my code:

let users
let sessions

export default class UsersDAO {
static async injectDB(conn) {
if (users && sessions) {
return
}
try {
users = await conn.db(process.env.MFLIX_NS).collection(“users”)
sessions = await conn.db(process.env.MFLIX_NS).collection(“sessions”)
} catch (e) {
console.error(Unable to establish collection handles in userDAO: ${e})
}
}

/**
Ticket: User Management

For this ticket, you will need to implement the following five methods:

  • getUser
  • addUser
  • loginUser
  • logoutUser
  • getUserSession

You can find these methods below this comment. Make sure to read the comments
in each method to better understand the implementation.

The method deleteUser is already given to you.
*/

/**

  • Finds a user in the users collection
  • @param {string} email - The email of the desired user
  • @returns {Object | null} Returns either a single user or nothing
    */
    static async getUser(email) {
    // TODO Ticket: User Management
    // Retrieve the user document corresponding with the user’s email.
    return await users.findOne({ user_id: email })
    }

/**

  • Adds a user to the users collection
  • @param {UserInfo} userInfo - The information of the user to add
  • @returns {DAOResponse} Returns either a “success” or an “error” Object
    */

static async addUser(userInfo) {
/**
Ticket: Durable Writes

Please increase the durability of this method by using a non-default write
concern with ``insertOne``.
*/

try {
  // TODO Ticket: User Management
  // Insert a user with the "name", "email", and "password" fields.
  let { name, email, password } = userInfo;
  // TODO Ticket: Durable Writes
  // Use a more durable Write Concern for this operation.
  let insertResult = await users.insertOne({ userInfo })
  return { insertResult: userInfo, success: true }
} catch (e) {
  if (String(e).startsWith("MongoError: E11000 duplicate key error")) {
    return { error: "A user with the given email already exists." }
  }
  console.error(`Error occurred while adding new user, ${e}.`)
  return { error: e }
}

}

/**

  • Adds a user to the sessions collection
  • @param {string} email - The email of the user to login
  • @param {string} jwt - A JSON web token representing the user’s claims
  • @returns {DAOResponse} Returns either a “success” or an “error” Object
    */
    static async loginUser(email, jwt) {
    try {
    // TODO Ticket: User Management
    // Use an UPSERT statement to update the “jwt” field in the document,
    // matching the “user_id” field with the email passed to this function.
    await sessions.updateOne(
    { user_id: email },
    { set: { jwt: jwt } }, { upsert: true } ) return { success: true } } catch (e) { console.error(`Error occurred while logging in user, {e}`)
    return { error: e }
    }
    }

/**

  • Removes a user from the sessions collection
  • @param {string} email - The email of the user to logout
  • @returns {DAOResponse} Returns either a “success” or an “error” Object
    */
    static async logoutUser(email) {
    try {
    // TODO Ticket: User Management
    // Delete the document in the sessions collection matching the email.
    await sessions.deleteOne({ user_id: email })
    return { success: true }
    } catch (e) {
    console.error(Error occurred while logging out user, ${e})
    return { error: e }
    }
    }

/**

  • Gets a user from the sessions collection
  • @param {string} email - The email of the user to search for in sessions
  • @returns {Object | null} Returns a user session Object, an “error” Object
  • if something went wrong, or null if user was not found.
    */
    static async getUserSession(email) {
    try {
    // TODO Ticket: User Management
    // Retrieve the session document corresponding with the user’s email.
    return sessions.findOne({ user_id: email })
    } catch (e) {
    console.error(Error occurred while retrieving user session, ${e})
    return null
    }
    }

/**

  • Removes a user from the sessions and users collections
  • @param {string} email - The email of the user to delete
  • @returns {DAOResponse} Returns either a “success” or an “error” Object
    */
    static async deleteUser(email) {
    try {
    await users.deleteOne({ email })
    await sessions.deleteOne({ user_id: email })
    if (!(await this.getUser(email)) && !(await this.getUserSession(email))) {
    return { success: true }
    } else {
    console.error(Deletion unsuccessful)
    return { error: Deletion unsuccessful }
    }
    }
    catch (e) {
    console.error(Error occurred while deleting user, ${e})
    return { error: e }
    }
    }

/**

  • Given a user’s email and an object of new preferences, update that user’s

  • data to include those preferences.

  • @param {string} email - The email of the user to update.

  • @param {Object} preferences - The preferences to include in the user’s data.

  • @returns {DAOResponse}
    /
    static async updatePreferences(email, preferences) {
    try {
    /
    *
    Ticket: User Preferences

    Update the “preferences” field in the corresponding user’s document to
    reflect the new information in preferences.
    */

    preferences = preferences || {}

    // TODO Ticket: User Preferences
    // Use the data in “preferences” to update the user’s preferences.
    const updateResponse = await users.updateOne(
    { someField: someValue },
    { $set: { someOtherField: someOtherValue } },
    )

    if (updateResponse.matchedCount === 0) {
    return { error: “No user found with that email” }
    }
    return updateResponse
    } catch (e) {
    console.error(
    An error occurred while updating this user's preferences, ${e},
    )
    return { error: e }
    }
    }

static async checkAdmin(email) {
try {
const { isAdmin } = await this.getUser(email)
return isAdmin || false
} catch (e) {
return { error: e }
}
}

static async makeAdmin(email) {
try {
const updateResponse = users.updateOne(
{ email },
{ $set: { isAdmin: true } },
)
return updateResponse
} catch (e) {
return { error: e }
}
}
}

/**

  • Parameter passed to addUser method
  • @typedef UserInfo
  • @property {string} name
  • @property {string} email
  • @property {string} password
    */

/**

  • Success/Error return object
  • @typedef DAOResponse
  • @property {boolean} [success] - Success
  • @property {string} [error] - Error
    */

And this is the specific problem:

static async addUser(userInfo) {
/**
Ticket: Durable Writes

Please increase the durability of this method by using a non-default write
concern with ``insertOne``.
*/

try {
  // TODO Ticket: User Management
  // Insert a user with the "name", "email", and "password" fields.
  let { name, email, password } = userInfo;
  // TODO Ticket: Durable Writes
  // Use a more durable Write Concern for this operation.
  let insertResult = await users.insertOne({ userInfo })
  return { insertResult: userInfo, success: true }
} catch (e) {
  if (String(e).startsWith("MongoError: E11000 duplicate key error")) {
    return { error: "A user with the given email already exists." }
  }
  console.error(`Error occurred while adding new user, ${e}.`)
  return { error: e }
}

}
And this is what is returned in my Terminal Console:

npm test -t user-management ⏎ ✭

server@1.0.0 test /Users/mariacam/Development/mflix-js
jest --passWithNoTests “user-management”

Determining test suites to run…Setup Mongo Connection
FAIL test/user-management.test.js
User Management
✕ it can add a new user to the database (45ms)
✓ it returns an error when trying to register duplicate user (23ms)
✓ it allows a user to login (58ms)
✓ it allows a user to logout (56ms)

● User Management › it can add a new user to the database

expect(received).toBeTruthy()

Received: undefined

  29 |      */
  30 |     const actual = await UsersDAO.addUser(testUser)
> 31 |     expect(actual.success).toBeTruthy()
     |                            ^
  32 |     expect(actual.error).toBeUndefined()
  33 | 
  34 |     // we should be able to get the user

  at toBeTruthy (test/user-management.test.js:31:28)
  at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
  at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
  at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
  at step (node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
  at node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13

Test Suites: 1 failed, 1 total
Tests: 1 failed, 3 passed, 4 total
Snapshots: 0 total
Time: 1.411s, estimated 3s
Ran all test suites matching /user-management/i.
Teardown Mongo Connection
npm ERR! Test failed. See above for more details.

I’m at a loss. This is the best outcome I have got so far.

So that’s WRONG.

I strongly suggest reading Destructuring assignment so you can learn WHY it’s wrong.

But basically you really should just extract the original file from the supplied course handout ( or use version control ) and compare your edits to the original and see what should have been there.

HINT: You did the right thing in the line above, but then ignored it in that line.
As the instruction says:

// Insert a user with the "name", "email", and "password" fields.

Thanks. The code

// TODO Ticket: Durable Writes
// Use a more durable Write Concern for this operation.
await users.insertOne({ userInfo })
return { success: true }

I got it like that. It also is for another ticket. So I have not been trying to fix that but above it. Maybe that was part of the problem.

To be clear, the { userInfo } is the part that is wrong. Hence the links, both here and on original threads. You basically inserted this:

{
  userInfo: { 
    name: "Magical Mr. Mistoffelees",
    email: "magicz@cats.com", 
    password: "somehashedpw",
  }
}

When you are supposed to be inserting:

{
  name: "Magical Mr. Mistoffelees",
  email: "magicz@cats.com",
  password: "somehashedpw",
}

Thanks Neil! But when I remove the curly brackets, it’s still failing. Now I get this error:

TypeError: Cannot convert undefined or null to object

  35 |     const user = await UsersDAO.getUser(testUser.email)
  36 |     // for comparison, we delete the _id key returned from Mongo
> 37 |     delete user._id
     |     ^
  38 |     expect(user).toEqual(testUser)
  39 |   })

Maybe if I walk away from this a bit. I have been going in circles with this one to the point of implementing stupid code.

That’s why the link to Destructuring was given to you.. There are several examples on that page.

As a further hint, you wrote this in yourself:

let { name, email, password } = userInfo;

So why are you not doing anything with name or email or password after you just defined them? Maybe you should do something with them!

I also suggest looking at the video just before this Ticket exercise and see how they are defining different fields in an object using insertOne().

I know! That was a last resort. I also created an object. I also tried insertResult and then adding let {n, ok} = insertResult.result. etc. Following my extensive notes from the video. And I broke the app. Part of it is, the way the comments are structured. I know it might sound stupid, but for this particular section, Durable writes is intertwined with user-management. And that is partially what is throwing me off. I even chaining promises. So I think I just have to take a break from this for a bit. I might see things more clearly after walking away for a spell.

Couple of things,

Durable Writes

Okay, the Ticket: Durable Writes is actually a “red herring” and can actually be ignored completely since it is really “optional”. The reasoning there is the actual ticket submission is not based on a unit test or verification from the /status route, but is instead implemented as a multiple choice question.

That said, you may implement it as part of the function if you really must, but it has no bearing on the test result. Of course that presumes you actually used one of the valid options from the multiple choice selections AND have at least a three node replica set running ( which is what the Atlas cluster you were instructed to set up IS ).

Basically, for the purposes of editing the usersDAO.js, you can completely ignore this with no consequence.

Adding a User

Now I might be misinformed here, but AFAIK the Disqus Forum Platform ( which is what this Q&A is using ) actually shows the count of how many people have visited a link as presented in a post. At least that is the case for other posts I have written here as I see that number presented in the UI.

What I see here is despite me including the Destructuring assignment - JavaScript | MDN link here in multiple comments, it does not appear to have been clicked on ( by you or anyone else ) a single time. ( As of writing )

So I apolgize if I’m misinformed here, but by the best information presented to me it does not appear as if you actually visited that link and read the content. The point of that link is to educate you on how to actually solve the part of this problem that you are still having difficulty with.

Therefore, I’ll attempt to explain briefly and in a less concise manner than the well maintained documentation…

Quick and Dirty Destructuring 101

For instance, you might define a variable like this:

var foo = 1;

Which simply assigns the value of 1 to the variable named foo.

Now we might just do something like attempt to define an Object with the key of foo and the value of 1, and we are even going to base that on our existing variable named foo from the previous statement:

var object = { foo };

Basically that statement uses foo as the key name ( taking the stringified representation of the name ) and the value which is assigned. It’s really just the same as writing in the long form:

var object = { 'foo': 1 };

Or even

var object = { 'foo': foo };

Noting you can also write { foo: foo } or { foo: 1 } as the rules for JavaScript Object Notation are that the left or key are taken as a “string” even without the specific notation as in the above examples.

In brief, { foo } is shorthand for { foo: foo } or the eventual translation of { 'foo': 1 } as modern JavaScript allows this assignment of a value from a variable without explicitly writing both the name of the key AND the value from the variable as used.

Now take the case of an Object with multiple keys:

var object = { one: 1, two: 2, three: 3 };

This is valid object notation, and based on what we know from the earlier examples you can also assign separate variables to construct if you wished. i.e:

var one = 1;
var two  = 2;
var three = 3;

// Shorthand construction
var object  = { one, two, three };

Which is the same as the explicit assignment, just broken into different variables.

This can also be done in reverse which is what we call "Destructuring" :

var { one, two, three } = { two: 2, three: 3, one: 1 };

Meaning that one, two and three have “destructured” the supplied Object into separate variables corresponding to the matching named keys with of course their assigned values.

console.log(one); // shows 1

In the same way you can reference object properties, as in:

console.log(object.one);    // shows 1

Or the whole Object:

console.log(object);   // shows { one: 1, two: 2, three: 3 }

And finally you can copy an Object to a new one:

var objectCopy = { ...object };
console.log(objectCopy);   // shows { one: 1, two: 2, three: 3 }

Or even partially:

 var { one, ...rest } = object;
 console.log(one);    // shows 1
 console.log(rest);    // shows { two: 2, three: 3 }

Which are uses for the ... or “spread operator”.


Like I said, “Quick and Dirty”. The Real Documentation is far more informative and concise, and really worth your time to read and understand.

NOT true. I think that you should reassess the basis of your accusation(s).

1 Like

@juliettet Please stop trying to cause arguments. All you appear to be doing here is causing trouble yourself, as I don’t see anything constructive submitted.

The “Full context” is that I do in fact see other posts here of my own with such “link click counters”, and this post is showing none.

The general basis is the repeated responses that do not appear to show any comprehension of the linked material, hence the effort taken

I also very clearly state my apologies is the impression is incorrect, but I am just going off the information I have and general observations of content.

You might therefore take more time yourself to actually read things in full rather than posting quick reactionary statements. If the wider context appears to be “offering help” then that’s generally what probably happening.

Thanks Neil. You were right this line (I checked because I had changed the code on the page so many times):

// TODO Ticket: Durable Writes
// Use a more durable Write Concern for this operation.
await users.insertOne({ someField: “someValue” })

I did click on your link (which I am very familiar with). And I am going to take your advice to continue my learning of MongoDB elsewhere. Thanks for your help!

1 Like

Well the reasoning behind why this was question ( as explained ) was that until this very view of the page that click counter has been non-existent. As of writing it says 1, so one person clicked the link.

The other part of reasoning is the different attempts here would indicate to me that you are not very familar or confident with Object Assignment or Destructuring at all, hence ALL of the effort to educate you, since that is the single part repeatedly demonstrated incorrectly in the attempts.

But as the saying goes, “You can lead a horse to water…” - So there’s the water, do with it what you will.

That’s NOT what I said at all.

You posted a different topic of What exactly is the relationship between usersDao.js and users.controller.js?

So in the context of talking about MVC Design Prinicples, part of my response was:

Which is true. Questions here should be about questions directly in the course, and the “How to Model and Controller relate (sic)” question was off-topic.

I was also calling you out in general since that topic was a “veiled” version of the later more direct thread which is this one, where you actually explained your real problem was understanding how to answer this specific addUser() method implementation.

But again, do with it as you will.

All,

A few things I’d like to say.

<Real Talk>
We build our courses in the hopes the majority of people that take them will be able to understand them. This means we may have to split things up that we otherwise wouldn’t, be more verbose than necessary, and go about doing things in a less than 100% performant manner if we feel it isolates the idea we’re trying to get across. Our hope is once the idea is understood the individual student will be able to use that going forward in whatever shape or form they wish.

We get questions that can astonish us, and sometimes the initial reaction can be to gnash teeth and take on a get gud attitude. However, we try to quickly check ourselves. One of the problems with knowing information is that you already know it. The human brain is exceedingly good at forgetting past pain, including the pain of learning. Every time someone asks a question I endeavor to put myself in their position and remember the pain of learning and the uncomfortableness of confusion. This helps to dismiss any feeling of superiority and soften a response.

I’ll share a little personal story with you guys. Prior to getting into engineering and education, I disarmed bombs for the Army. In my spare time I taught myself, taking courses here on MongoU to learn MongoDB, other sites to learn other databases, and classes from Coursera, Edx, and Udacity to learning about computer science. I got involved in open source where I learned a lot about application and learned a lot from my peers. One of the things that helped the most was the ability to ask what I now would consider rudimentary questions without fear of being derided or discouraged in some way.

Inclusion isn’t only about gender, religion, orientation, ethnicity, or any of the other myriad facets which we find to differentiate ourselves from others and doing our best to respect and ignore them at the same time. It’s about being patient, trying our best to see things from the other person’s perspective and from the other person’s experience.

Be patient with each other. Be forgiving. If you feel someone is fishing for the answer, simply flag the post and move on. Someone who really isn’t in it for the learning probably isn’t going to read a response chastising them for answer-seeking, and if they do most likely isn’t going to be swayed to change their ways while they skim the response and dismiss it because it doesn’t help give them what they want.
</Real Talk>

10 Likes

Hi Guys,

thank you for this discussion. Sorry for interrupting your discussion but I hope I am on the right spot here.
I have the following code:

 let insertResult = await users.insertOne({
        name: userInfo.name,
        email: userInfo.email,
        password: userInfo.password,
        })

  console.log(insertResult.insertedCount)
  console.log(insertResult.insertedID)

  return { success: true }

… and the insertedCount is apparently 1, the ID is undefined and I am getting the following message:

● User Management › it can add a new user to the database

TypeError: Cannot convert undefined or null to object

  35 |     const user = await UsersDAO.getUser(testUser.email)
  36 |     // for comparison, we delete the _id key returned from Mongo
> 37 |     delete user._id
     |     ^
  38 |     expect(user).toEqual(testUser)
  39 |   })
  40 |

Can you give me a hint?
Thank you!

3 Likes

Hello everyone,

Any news on Jonas_29550 question?

I am having the same issue. Have checked every related post on the forum and still have not found an answer as to why the first test fails…

What is the correct code? At this point, I would just like to know the answer. I have been trying to figure out for hours…

I will sincerely appreciate any feedback / help.

Thank you.

1 Like

remove the comma after password to see if it helps.

Hi @Szilard_74059,

I am still stuck on this as well. I’ve tried many configurations and nothing works. I am unable to complete many of the tickets that follow this one as a result.

Thanks for highlighting this. It seems like at least a few of us have spent a considerable amount of time on this.

Hi @Nu_81142,

…still doesn’t work …

Thanks

This is how I made myself to understand and complete the ticket:
In order to complete this ticket

  1. Need to have a clear distinguish the two collections and know the fields belong to the them. E.g.,
    users.name, users.email, users.password
    sessions.jwt, sessions.user_id
  2. From there when in the await
    e.g., await sessions.findOne you know which field should be called
  3. In the updateOne()
    You should distinguish the two blocks, one is for lookup and the other is for update ($set)
    updateOne(
    {fields to be looked up },
    {$set: fields whose value to be updated}
    )
    There’s a good example to look at in the lecture: In the theatre collection, look up the document that has field theaterId that has value equal 8 and update the document location.address.street field value to “14161……”
3 Likes

Thanks @Nu_81142!

I’ve (finally) got it working!

@Jonas_29550 and @Szilard_74059,

Check out this thread. There is a lot of helpful info here: