sei-ec-remote / team-project-issues

0 stars 0 forks source link

Writing a patch route for a subdocument of user #100

Closed zyaffee closed 2 years ago

zyaffee commented 2 years ago

Describe the bug A clear and concise description of what the bug is. My route for patching a subdocument, which perhaps should be a mongoose map instead, will not work in postman or live

What is the problem you are trying to solve? I am trying to update a field of user that holds stats about their performance called playerStats, that field is currently a subdocument with its own model with key-value pairs of relevant fields, all Numbers except for owner, but perhaps should be a mongoose map instead. I am currently trying to solve this problem by writing a user route that finds a user by ID and sets their playerStats.

Expected behavior A clear and concise description of what you expected to happen. The playerStats subdocument changes in accordance with request body

What is the actual behavior? A clear and concise description of what actually happened. 500 internal server error, I get back a json response of {}

Post any code you think might be relevant (one fenced block per file) This is what a playerStats model looks like:

const playerStatsSchema = new mongoose.Schema(
    {
        'General Knowledge': {
            type: Number,
            required: true,
            default: 0
        },
        'Entertainment: Books': {
            type: Number,
            required: true,
            default: 0
        },
        'Entertainment: Film': {
            type: Number,
            required: true,
            default: 0
        },
        ...
        ...
        owner: {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        }
    }
)

This is the route I am writing in its current form

// CHANGE stats
// PATCH
router.patch('/:userId', requireToken, (req, res, next) => {
    const { userId } = req.params

    User.findById(userId)
        .then(handle404)
        .then(user => {
            const stats = user.playerStats

            requireOwnership(req, user)

            stats.set(req.body.playerStats)

            return user.save()
        })
        .then(() => res.sendStatus(204))
        .catch(next)
})

This is the request body in json I'm sending through postman:

{"playerStats": {
    "General Knowledge": 0,
    "Entertainment: Books": 0,
    "Entertainment: Film": 0,
    "Entertainment: Music": 0,
    "Entertainment: Musicals & Theatres": 0,
    "Entertainment: Television": 0,
    "Entertainment: Video Games": 0,
    "Entertainment: Board Games": 0,
    "Science & Nature": 0,
    "Science: Computers": 0,
    "Science: Mathematics": 0,
    "Science: Gadgets": 0,
    "Mythology": 0,
    "Sports": 0,
    "Geography": 0,
    "History": 0,
    "Politics": 0,
    "Art": 0,
    "Celebrities": 0,
    "Animals": 0,
    "Vehicles": 0,
    "Entertainment: Comics": 0,
    "Entertainment: Japanese Anime & Manga": 0,
    "Entertainment: Cartoon & Animations": 0
}}

What is your best guess as to the source of the problem? The format of the route is wrong

What things have you already tried to solve the problem? Several route formats and req body formats

Additional context Add any other context about the problem here. The reason I'm unsure if this functionality of PATCHing the user subdoc should be a route is because it is not to be accessed by the user at all but called automatically when the user answers a trivia question.

Paste a link to your repository here https://github.com/zyaffee/Trivia-API/tree/main

timmshinbone commented 2 years ago

Let me see the user model

zyaffee commented 2 years ago
const mongoose = require('mongoose')
const playerStatsSchema = require('./playerStats')

const userSchema = new mongoose.Schema(
    {
        email: {
            type: String,
            required: true,
            unique: true,
        },
        hashedPassword: {
            type: String,
            required: true,
        },
        playerStats: [playerStatsSchema],
        flaggedQuestions: {
            type: Array,
            required: true
        },
        token: String
    },
    {
        timestamps: true,
        // this is a validator, it removes the password field from the object when returning a user
        toObject: {
            // remove `hashedPassword` field when we call `.toObject`
            transform: (_doc, user) => {
                delete user.hashedPassword
                return user
            },
        },
        toJSON: {
            // remove `hashedPassword` field when we call `.toObject`
            transform: (_doc, user) => {
                delete user.hashedPassword
                return user
            }
        }
    }
)

userSchema.virtual('username').get(function() {
    return this.email.slice(0, this.email.indexOf('@'))
})

// TODO -> Virtuals that use playerStats to return leaderboard relevant data
//separating user/owner is not necessary- conditional is post request?
//user should own/edit games w/ questions pulled from API-user form similar to API
// TODO -> Virtuals that use playerStats to return leaderboard relevant data

module.exports = mongoose.model('User', userSchema)
timmshinbone commented 2 years ago

Ok, and what is the response you get when trying to send the req with postman?

zyaffee commented 2 years ago

image

I realize the empty response is normal but I'm not sure why I'm getting a 500 internal server error

zyaffee commented 2 years ago
MongoServerError: Cannot create field '[object Object]' in element {playerStats: []}

New error from my CL after editing my route to the following:

// CHANGE stats
// PATCH
router.patch('/:userId', requireToken, (req, res, next) => {
    const { userId } = req.params

    User.findById(userId)
        .then(user => {
            const stats = user.playerStats

            stats.set(req.body.playerStats)

            return user.save()
        })
        .then(() => res.sendStatus(204))
        .catch(next)
})
zyaffee commented 2 years ago

this indicates to me that formatting the subdocument as

playerStats: [playerStatsSchema],

in my user model is incorrect

timmshinbone commented 2 years ago

Your route should use console logs to figure out this error:

MongoServerError: Cannot create field '[object Object]' in element {playerStats: []}

If it's trying to use '[object Object]' that means the data is not formatted quite right. console log the whole request body, send again, and share the output

zyaffee commented 2 years ago

user.playerStats = req.body.playerStats

this line worked, but will be reworking schema to best practice so that patch can work as intended