The Scoop Project - TypeError: Cannot set property '1' of undefined

Hey there everyone! I’m looking into my code and the biggest error I’m getting is "cannot set property ‘1’ of undefined. It seems like on the Scoop Project, I’m unable to alter the value of the let nextCommentId property of the database object created at the top of the project. It seems weird since it’s a let object and all I’m doing is ++ing it.
Code comes first, error second!
Really appreciate this community :slight_smile:
Lucas

// database is let instead of const to allow us to modify it in test.js
let database = {
  users: {},
  articles: {},
  nextArticleId: 1,
  comments: {},
  nextCommentId: 1
};


const routes = {
  '/users': {
    'POST': getOrCreateUser
  },
  '/users/:username': {
    'GET': getUser
  },
  '/articles': {
    'GET': getArticles,
    'POST': createArticle
  },
  '/articles/:id': {
    'GET': getArticle,
    'PUT': updateArticle,
    'DELETE': deleteArticle
  },
  '/articles/:id/upvote': {
    'PUT': upvoteArticle
  },
  '/articles/:id/downvote': {
    'PUT': downvoteArticle
  },
  '/comments': {
    'POST': createComment
  },
  '/comments/:id': {
    'PUT': updateComment,
    'DELETE': deleteComment
  },
  '/comments/:id/upvote': {
    'PUT': upvoteComment
  },
  '/comments/:id/downvote': {
    'PUT': downvoteComment
  }
};

function createComment(url, request) {
  const requestComment =request.body && request.body.comment;
  const response = {};

  if (requestComment && requestComment.body &&
    requestComment.articleId && database.articles[requestComment.articleId] &&
    requestComment.username && database.users[requestComment.username])
  {
    const comment = {
      id: database.nextCommentId++,
      body: requestComment.body,
      username: requestComment.username,
      articleId: requestComment.articleId,
      upvotedBy: [],
      downvotedBy: []
    };

    database.comments[comment.id] = comment;
    database.users[comment.username].commentId.unshift(comment);
    database.articles[comment.articleId].commentIds.unshift(comment.id);

    response.body = {comment: comment};
    response.status = 201;
  } else {
    return response.status = 400;
  }
  return response;
}

function updateComment (url, request) {
  const id = Number(url.split('/').filter(segment => segment)[1]);
  const savedComment = database.comment[id];
  const requestComment = request.body && request.body.comment;
  const response = {};
  if (!requestComment.body || !requestComment) {
    response.status = 400;
  } else if (!savedComment) {
    response.status = 404;
  } else {
    savedComment.body = requestComment.body;

    response.body = {comment: savedComment};
    response.status = 200;
  }
  return response;
}

function deleteComment (url, request) {
  const id = Number(url.split('/').filter(segment => segment)[1]);
  const savedComment = database.comments[id];
  const response = {};
  if (savedComment)
  {
    database.comment[id] = null;
    const articleCommentIds = database.articles[savedComment.articleId].commentIds;
    articleCommentIds.splice(articleCommentIds.IndexOf(id), 1);
    const userCommentIds = database.users[savedComment.username].commentIds;
    userCommentIds.splice(userCommentIds.IndexOf(id), 1);
    response.status = 204;
  } else {
   
    response.status = 404;
    
  }
  return response;
}

function upvoteComment (url, request) {
const id = Number(url.split('/').filter(segment => segment)[1]);
const username = request.body && request.body.username;
let savedComment = database.comments[id];
const response = {};

if (savedComment && database.users[username]){
  savedComment = upvote(savedComment, username);

  response.body = {comment : savedComment};
response.status = 200;
} else {
  response.status = 400;
}
return response;
}

function downvoteComment (url, request) {
  const id = Number(url.split('/').filter(segment => segment)[1]);
  const username = request.body && request.body.username;
  let savedComment = database.comments[id];
  const response = {};
  if (savedComment && database.users[username]){
    savedComment = downvote(savedComment, username);

    response.body = {comment: savedComment};
    response.status = 200;
  
  } else {
    response.status = 400;
  }
  return response;
}

function getUser(url, request) {
  const username = url.split('/').filter(segment => segment)[1];
  const user = database.users[username];
  const response = {};

  if (user) {
    const userArticles = user.articleIds.map(
        articleId => database.articles[articleId]);
    const userComments = user.commentIds.map(
        commentId => database.comments[commentId]);
    response.body = {
      user: user,
      userArticles: userArticles,
      userComments: userComments
    };
    response.status = 200;
  } else if (username) {
    response.status = 404;
  } else {
    response.status = 400;
  }

  return response;
}

function getOrCreateUser(url, request) {
  const username = request.body && request.body.username;
  const response = {};

  if (database.users[username]) {
    response.body = {user: database.users[username]};
    response.status = 200;
  } else if (username) {
    const user = {
      username: username,
      articleIds: [],
      commentIds: []
    };
    database.users[username] = user;

    response.body = {user: user};
    response.status = 201;
  } else {
    response.status = 400;
  }

  return response;
}

function getArticles(url, request) {
  const response = {};

  response.status = 200;
  response.body = {
    articles: Object.keys(database.articles)
        .map(articleId => database.articles[articleId])
        .filter(article => article)
        .sort((article1, article2) => article2.id - article1.id)
  };

  return response;
}

function getArticle(url, request) {
  const id = Number(url.split('/').filter(segment => segment)[1]);
  const article = database.articles[id];
  const response = {};

  if (article) {
    article.comments = article.commentIds.map(
      commentId => database.comments[commentId]);

    response.body = {article: article};
    response.status = 200;
  } else if (id) {
    response.status = 404;
  } else {
    response.status = 400;
  }

  return response;
}

function createArticle(url, request) {
  const requestArticle = request.body && request.body.article;
  const response = {};

  if (requestArticle && requestArticle.title && requestArticle.url &&
      requestArticle.username && database.users[requestArticle.username]) {
    const article = {
      id: database.nextArticleId++,
      title: requestArticle.title,
      url: requestArticle.url,
      username: requestArticle.username,
      commentIds: [],
      upvotedBy: [],
      downvotedBy: []
    };

    database.articles[article.id] = article;
    database.users[article.username].articleIds.push(article.id);

    response.body = {article: article};
    response.status = 201;
  } else {
    response.status = 400;
  }

  return response;
}

function updateArticle(url, request) {
  const id = Number(url.split('/').filter(segment => segment)[1]);
  const savedArticle = database.articles[id];
  const requestArticle = request.body && request.body.article;
  const response = {};

  if (!id || !requestArticle) {
    response.status = 400;
  } else if (!savedArticle) {
    response.status = 404;
  } else {
    savedArticle.title = requestArticle.title || savedArticle.title;
    savedArticle.url = requestArticle.url || savedArticle.url;

    response.body = {article: savedArticle};
    response.status = 200;
  }

  return response;
}

function deleteArticle(url, request) {
  const id = Number(url.split('/').filter(segment => segment)[1]);
  const savedArticle = database.articles[id];
  const response = {};

  if (savedArticle) {
    database.articles[id] = null;
    savedArticle.commentIds.forEach(commentId => {
      const comment = database.comments[commentId];
      database.comments[commentId] = null;
      const userCommentIds = database.users[comment.username].commentIds;
      userCommentIds.splice(userCommentIds.indexOf(id), 1);
    });
    const userArticleIds = database.users[savedArticle.username].articleIds;
    userArticleIds.splice(userArticleIds.indexOf(id), 1);
    response.status = 204;
  } else {
    response.status = 400;
  }

  return response;
}

function upvoteArticle(url, request) {
  const id = Number(url.split('/').filter(segment => segment)[1]);
  const username = request.body && request.body.username;
  let savedArticle = database.articles[id];
  const response = {};

  if (savedArticle && database.users[username]) {
    savedArticle = upvote(savedArticle, username);

    response.body = {article: savedArticle};
    response.status = 200;
  } else {
    response.status = 400;
  }

  return response;
}

function downvoteArticle(url, request) {
  const id = Number(url.split('/').filter(segment => segment)[1]);
  const username = request.body && request.body.username;
  let savedArticle = database.articles[id];
  const response = {};

  if (savedArticle && database.users[username]) {
    savedArticle = downvote(savedArticle, username);

    response.body = {article: savedArticle};
    response.status = 200;
  } else {
    response.status = 400;
  }

  return response;
}

function upvote(item, username) {
  if (item.downvotedBy.includes(username)) {
    item.downvotedBy.splice(item.downvotedBy.indexOf(username), 1);
  }
  if (!item.upvotedBy.includes(username)) {
    item.upvotedBy.push(username);
  }
  return item;
}

function downvote(item, username) {
  if (item.upvotedBy.includes(username)) {
    item.upvotedBy.splice(item.upvotedBy.indexOf(username), 1);
  }
  if (!item.downvotedBy.includes(username)) {
    item.downvotedBy.push(username);
  }
  return item;
}

// Write all code above this line.

const http = require('http');
const url = require('url');

const port = process.env.PORT || 4000;
const isTestMode = process.env.IS_TEST_MODE;

const requestHandler = (request, response) => {
  const url = request.url;
  const method = request.method;
  const route = getRequestRoute(url);

  if (method === 'OPTIONS') {
    var headers = {};
    headers["Access-Control-Allow-Origin"] = "*";
    headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS";
    headers["Access-Control-Allow-Credentials"] = false;
    headers["Access-Control-Max-Age"] = '86400'; // 24 hours
    headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept";
    response.writeHead(200, headers);
    return response.end();
  }

  response.setHeader('Access-Control-Allow-Origin', '*');
  response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  response.setHeader(
      'Access-Control-Allow-Headers', 'X-Requested-With,content-type');

  if (!routes[route] || !routes[route][method]) {
    response.statusCode = 400;
    return response.end();
  }

  if (method === 'GET' || method === 'DELETE') {
    const methodResponse = routes[route][method].call(null, url);
    !isTestMode && (typeof saveDatabase === 'function') && saveDatabase();

    response.statusCode = methodResponse.status;
    response.end(JSON.stringify(methodResponse.body) || '');
  } else {
    let body = [];
    request.on('data', (chunk) => {
      body.push(chunk);
    }).on('end', () => {
      body = JSON.parse(Buffer.concat(body).toString());
      const jsonRequest = {body: body};
      const methodResponse = routes[route][method].call(null, url, jsonRequest);
      !isTestMode && (typeof saveDatabase === 'function') && saveDatabase();

      response.statusCode = methodResponse.status;
      response.end(JSON.stringify(methodResponse.body) || '');
    });
  }
};

const getRequestRoute = (url) => {
  const pathSegments = url.split('/').filter(segment => segment);

  if (pathSegments.length === 1) {
    return `/${pathSegments[0]}`;
  } else if (pathSegments[2] === 'upvote' || pathSegments[2] === 'downvote') {
    return `/${pathSegments[0]}/:id/${pathSegments[2]}`;
  } else if (pathSegments[0] === 'users') {
    return `/${pathSegments[0]}/:username`;
  } else {
    return `/${pathSegments[0]}/:id`;
  }
}

if (typeof loadDatabase === 'function' && !isTestMode) {
  const savedDatabase = loadDatabase();
  if (savedDatabase) {
    for (key in database) {
      database[key] = savedDatabase[key] || database[key];
    }
  }
}

const server = http.createServer(requestHandler);

server.listen(port, (err) => {
  if (err) {
    return console.log('Server did not start succesfully: ', err);
  }

  console.log(`Server is listening on ${port}`);
});
Server is listening on 8080
  Scoop - server.js:
    Database Setup
      √ database should have a property called comments initialized to an object
      √ database should have a property called nextCommentId initialized to 1
    Routes Setup
      √ routes should have a property called /comments initialized to an object
      √ routes should have a property called /comments/:id initialized to an object
      √ routes should have a property called /comments/:id/upvote initialized to an object
      √ routes should have a property called /comments/:id/downvote initialized to an object
    /comments POST
      √ routes['/comments'] should have a method called POST
      1) routes['/comments'].POST should create a new comment
      2) routes['/comments'].POST should increment the next comment ID
      3) routes['/comments'].POST should add a newly created comment's ID to the author's comment IDs
      4) routes['/comments'].POST should add a newly created comment's ID to the article's comment IDs
      5) routes['/comments'].POST should return a 201 response containing a newly-created comment
      6) routes['/comments'].POST should return a 400 response if a required field is not supplied
      7) routes['/comments'].POST should return a 400 response if the comment's user does not exist
      8) routes['/comments'].POST should return a 400 response if the comment's article does not exist
      9) routes['/comments'].POST should return a 400 response when given a request with no body
      10) routes['/comments'].POST should return a 400 response when given a request with no comment
    /comments/:id PUT
      √ routes['/comments/:id'] should have a method called PUT
      11) routes['/comments/:id'].PUT should update an existing comment
      12) routes['/comments/:id'].PUT should not update uneditable fields
      13) routes['/comments/:id'].PUT should not update fields to invalid values
      14) routes['/comments/:id'].PUT should return a 200 response after a succesful update
      15) routes['/comments/:id'].PUT should return a 404 response with a non-existent comment
      16) routes['/comments/:id'].PUT should return a 400 response when not supplied a request body
      17) routes['/comments/:id'].PUT should return a 400 response when not supplied an updated comment
    /comments/:id DELETE
      √ routes['/comments/:id'] should have a method called DELETE
      18) routes['/comments/:id'].DELETE should delete an existing comment
      19) routes['/comments/:id'].DELETE should remove a deleted comment ID from the author's comment IDs
      20) routes['/comments/:id'].DELETE should remove a deleted comment ID from the article's comment IDs
      21) routes['/comments/:id'].DELETE should return a 204 response after a succesful delete
      √ routes['/comments/:id'].DELETE should return a 404 response with a non-existent comment ID
    /commnets/:id/upvote PUT
      √ routes['/comments/:id/upvote'] should have a method called PUT
      √ routes['/comments/:id/upvote'].PUT should upvote an comment
      √ routes['/comments/:id/upvote'].PUT should not allow a user to upvote multiple times
      √ routes['/comments/:id/upvote'].PUT should remove a user from downvotedBy if they switch to upvote
      √ routes['/comments/:id/upvote'].PUT should return a 200 response after a succesful upvote and send back the upvoted comment      
      √ routes['/comments/:id/upvote'].PUT should return a 400 response when supplied a non-existent user
      √ routes['/comments/:id/upvote'].PUT should return a 400 response when supplied a non-existent comment ID
      √ routes['/comments/:id/upvote'].PUT should return a 400 response when given no username
      √ routes['/comments/:id/upvote'].PUT should return a 400 response when given no request body
    /comments/:id/downvote PUT
      √ routes['/comments/:id/downvote'] should have a method called PUT
      √ routes['/comments/:id/downvote'].PUT should downvote an comment
      √ routes['/comments/:id/downvote'].PUT should not allow a user to downvote multiple times
      √ routes['/comments/:id/downvote'].PUT should remove a user from upvotedBy if they switch to downvote
      √ routes['/comments/:id/downvote'].PUT should return a 200 response after a succesful downvote and send back the downvoted comment      √ routes['/comments/:id/downvote'].PUT should return a 400 response when supplied a non-existent user
      √ routes['/comments/:id/downvote'].PUT should return a 400 response when supplied a non-existent comment ID
      √ routes['/comments/:id/downvote'].PUT should return a 400 response when given no username
      √ routes['/comments/:id/downvote'].PUT should return a 400 response when given no request body


  28 passing (74ms)
  21 failing

  1) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should create a new comment:
     TypeError: Cannot read property 'unshift' of undefined
      at Object.createComment [as POST] (server.js:66:48)
      at Context.<anonymous> (test\test.js:86:34)
      at processImmediate (internal/timers.js:456:21)

  2) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should increment the next comment ID:
     TypeError: Cannot read property 'unshift' of undefined
      at Object.createComment [as POST] (server.js:66:48)
      at Context.<anonymous> (test\test.js:101:34)
      at processImmediate (internal/timers.js:456:21)

  3) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should add a newly created comment's ID to the author's comment IDs:
     TypeError: Cannot read property 'unshift' of undefined
      at Object.createComment [as POST] (server.js:66:48)
      at Context.<anonymous> (test\test.js:112:34)
      at processImmediate (internal/timers.js:456:21)

  4) Scoop - server.js: 
       /comments POST
         routes['/comments'].POST should add a newly created comment's ID to the article's comment IDs:
     TypeError: Cannot read property 'unshift' of undefined
      at Object.createComment [as POST] (server.js:66:48)
      at Context.<anonymous> (test\test.js:123:34)
      at processImmediate (internal/timers.js:456:21)

  5) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should return a 201 response containing a newly-created comment:
     TypeError: Cannot read property 'unshift' of undefined
      at Object.createComment [as POST] (server.js:66:48)
      at Context.<anonymous> (test\test.js:134:51)
      at processImmediate (internal/timers.js:456:21)

  6) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should return a 400 response if a required field is not supplied:
     AssertionError: expected undefined to equal 400
      at Context.<anonymous> (test\test.js:155:34)
      at processImmediate (internal/timers.js:456:21)

  7) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should return a 400 response if the comment's user does not exist:
     AssertionError: expected undefined to equal 400
      at Context.<anonymous> (test\test.js:181:34)
      at processImmediate (internal/timers.js:456:21)

  8) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should return a 400 response if the comment's article does not exist:
     AssertionError: expected undefined to equal 400
      at Context.<anonymous> (test\test.js:193:34)
      at processImmediate (internal/timers.js:456:21)

  9) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should return a 400 response when given a request with no body:
     AssertionError: expected undefined to equal 400
      at Context.<anonymous> (test\test.js:200:34)
      at processImmediate (internal/timers.js:456:21)

  10) Scoop - server.js:
       /comments POST
         routes['/comments'].POST should return a 400 response when given a request with no comment:
     AssertionError: expected undefined to equal 400
      at Context.<anonymous> (test\test.js:207:34)
      at processImmediate (internal/timers.js:456:21)

  11) Scoop - server.js:
       /comments/:id PUT
         routes['/comments/:id'].PUT should update an existing comment:
     TypeError: Cannot read property '1' of undefined
      at Object.updateComment [as PUT] (server.js:79:40)
      at Context.<anonymous> (test\test.js:234:37)
      at processImmediate (internal/timers.js:456:21)

  12) Scoop - server.js:
       /comments/:id PUT
         routes['/comments/:id'].PUT should not update uneditable fields:
     TypeError: Cannot read property '1' of undefined
      at Object.updateComment [as PUT] (server.js:79:40)
      at Context.<anonymous> (test\test.js:246:37)
      at processImmediate (internal/timers.js:456:21)

  13) Scoop - server.js: 
       /comments/:id PUT
         routes['/comments/:id'].PUT should not update fields to invalid values:
     TypeError: Cannot read property '1' of undefined
      at Object.updateComment [as PUT] (server.js:79:40)
      at Context.<anonymous> (test\test.js:260:37)
      at processImmediate (internal/timers.js:456:21)

  14) Scoop - server.js:
       /comments/:id PUT
         routes['/comments/:id'].PUT should return a 200 response after a succesful update:
     TypeError: Cannot read property '1' of undefined
      at Object.updateComment [as PUT] (server.js:79:40)
      at Context.<anonymous> (test\test.js:272:54)
      at processImmediate (internal/timers.js:456:21)

  15) Scoop - server.js:
       /comments/:id PUT
         routes['/comments/:id'].PUT should return a 404 response with a non-existent comment:
     TypeError: Cannot read property '2' of undefined
      at Object.updateComment [as PUT] (server.js:79:40)
      at Context.<anonymous> (test\test.js:285:54)
      at processImmediate (internal/timers.js:456:21)

  16) Scoop - server.js: 
       /comments/:id PUT
         routes['/comments/:id'].PUT should return a 400 response when not supplied a request body:
     TypeError: Cannot read property '1' of undefined
      at Object.updateComment [as PUT] (server.js:79:40)
      at Context.<anonymous> (test\test.js:298:54)
      at processImmediate (internal/timers.js:456:21)

  17) Scoop - server.js: 
       /comments/:id PUT
         routes['/comments/:id'].PUT should return a 400 response when not supplied an updated comment:
     TypeError: Cannot read property '1' of undefined
      at Object.updateComment [as PUT] (server.js:79:40)
      at Context.<anonymous> (test\test.js:311:54)
      at processImmediate (internal/timers.js:456:21)

  18) Scoop - server.js:
       /comments/:id DELETE
         routes['/comments/:id'].DELETE should delete an existing comment:
     TypeError: Cannot set property '1' of undefined
      at Object.deleteComment [as DELETE] (server.js:101:26)
      at Context.<anonymous> (test\test.js:353:40)
      at processImmediate (internal/timers.js:456:21)

  19) Scoop - server.js:
       /comments/:id DELETE
         routes['/comments/:id'].DELETE should remove a deleted comment ID from the author's comment IDs:
     TypeError: Cannot set property '1' of undefined
      at Object.deleteComment [as DELETE] (server.js:101:26)
      at Context.<anonymous> (test\test.js:359:40)
      at processImmediate (internal/timers.js:456:21)

  20) Scoop - server.js:
       /comments/:id DELETE
         routes['/comments/:id'].DELETE should remove a deleted comment ID from the article's comment IDs:
     TypeError: Cannot set property '1' of undefined
      at Object.deleteComment [as DELETE] (server.js:101:26)
      at Context.<anonymous> (test\test.js:365:40)
      at processImmediate (internal/timers.js:456:21)

  21) Scoop - server.js:
       /comments/:id DELETE
         routes['/comments/:id'].DELETE should return a 204 response after a succesful delete:
     TypeError: Cannot set property '1' of undefined
      at Object.deleteComment [as DELETE] (server.js:101:26)
      at Context.<anonymous> (test\test.js:371:57)
      at processImmediate (internal/timers.js:456:21)

I am doing this project as well and I think on your “createComment” function, the line “database.users[comment.username].commentId.unshift(comment);” you may want to change the ‘comment’ to ‘comment.id’ since the test want you to do so. Also, the property of database.users[comment.username] would be commentIds like the one you put in the next line.

  1. routes[’/comments’].POST should add a newly created comment’s ID to the author’s comment IDs
    (this is the test I was talking about, hope it helps!)
1 Like