From 9790b58efdd0058c5fb9a7cb46a9ae12a82c98c3 Mon Sep 17 00:00:00 2001 From: Spazcool Date: Thu, 14 Dec 2017 13:07:35 +0800 Subject: [PATCH 01/16] REFACTOR: logScanned to catch doubled UUIDs & render doubles on template --- app.js | 26 +++++++++++++++++++------- views/recent.ejs | 12 +++++++++++- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app.js b/app.js index e63ab03..2a602c0 100644 --- a/app.js +++ b/app.js @@ -8,18 +8,30 @@ const app = express(); const allScans = []; -function logScanned(uuid, fixture) { +function logScanned(uuid, allMatches) { + const now = new Date(); // TRICK: fixture tells if the the uuid was found in database or not if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(uuid)) { return; } - const now = new Date(); - if (!fixture) { - allScans.unshift({ time: now, status: 'missing', uuid }); + allScans.map(item => item.status = (item.uuid === uuid) ? 'fixed' : item.status); + if (allMatches.length > 1) { + allMatches.map((item, index) => + allScans.unshift({ + time: now, + fixture: allMatches[index].fixture ? allMatches[index].fixture : '', + status: allMatches[index].fixture ? '' : 'missing', + uuid, + double: true, + })); return; } - allScans.map(item => item.status = (item.uuid === uuid) ? 'fixed' : item.status); - allScans.unshift({ time: now, fixture, uuid }); + allScans.unshift({ + time: now, + fixture: allMatches[0].fixture ? allMatches[0].fixture : '', + status: allMatches[0].fixture ? '' : 'missing', + uuid, + }); } app.set('view engine', 'ejs'); @@ -65,7 +77,7 @@ app.get('/:uuid', (req, res) => { }); return; } - logScanned(req.params.uuid, matches[0].fixture); + logScanned(req.params.uuid, matches); matches[0].similarItems = searchDatabase({ fixture: matches[0].fixture }, allItems) .filter(item => item.uuid !== matches[0].uuid) .splice(0, 3); diff --git a/views/recent.ejs b/views/recent.ejs index 372cf57..973e3c4 100644 --- a/views/recent.ejs +++ b/views/recent.ejs @@ -3,11 +3,16 @@

Recently scanned QR code

+
<% include ./partials/footer %> From 93da255863d6ce2f9cabc92cd73237dedfa5403f Mon Sep 17 00:00:00 2001 From: Spazcool Date: Wed, 20 Dec 2017 03:20:41 +0800 Subject: [PATCH 06/16] ADD: double UUID conditional without duplicate code --- views/recent.ejs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/views/recent.ejs b/views/recent.ejs index ca2b0b1..dc1f5bd 100644 --- a/views/recent.ejs +++ b/views/recent.ejs @@ -1,21 +1,21 @@ <% include ./partials/header %> -

Recently scanned QR code

+

Recently scanned QR codes

From cae791df155749d80e8470c86dc9cb1ef9bff900 Mon Sep 17 00:00:00 2001 From: Spazcool Date: Wed, 20 Dec 2017 06:03:01 +0800 Subject: [PATCH 07/16] ADD: double UUID conditional without duplicate code --- app.js | 4 ++-- googleSpreadsheet.js | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index fa17176..82580c7 100644 --- a/app.js +++ b/app.js @@ -40,8 +40,8 @@ app.get(['/favicon.ico', '/robots.txt'], (req, res) => { app.get('/search', (req, res) => { loadDatabase((allItems) => { res.render('search', { - matches: searchDatabase(req.query, allItems) - .sort((a, b) => (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)), + matches: searchDatabase(req.query, allItems).sort((a, b) => + (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)), }); }); }); diff --git a/googleSpreadsheet.js b/googleSpreadsheet.js index 4a16a68..f32210d 100644 --- a/googleSpreadsheet.js +++ b/googleSpreadsheet.js @@ -1,11 +1,12 @@ const google = require('googleapis'); const keys = require('./config/keys'); -function rowToObject(val, lab) { +function rowToObject(val, lab, index) { const o = {}; for (let i = 0; i < lab.length; i += 1) { o[lab[i]] = val[i]; } + o.cellRef = 'https://docs.google.com/spreadsheets/d/1QHKa3vUpht7zRl_LEzl3BlUbolz3ZiL8yKHzdBL42dY/edit#gid=0&range=A' + index; return o; } @@ -20,8 +21,8 @@ function loadDatabase(callback) { console.log(`The API returned an error: ${err}`); return; } - return callback(response.values.map(row => - rowToObject(row, response.values[0])).splice(1)); + return callback(response.values.map((row, index) => + rowToObject(row, response.values[0], index)).splice(1)); }); } From 5b379120f875d8b6a520a5781ff350bc9c792b40 Mon Sep 17 00:00:00 2001 From: Spazcool Date: Wed, 20 Dec 2017 06:46:07 +0800 Subject: [PATCH 08/16] ADD: link to item row in spreadsheet to both item obj and recent.ejs --- app.js | 4 +++- googleSpreadsheet.js | 4 ++-- views/recent.ejs | 15 +++++++++------ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app.js b/app.js index 82580c7..8bd2a6c 100644 --- a/app.js +++ b/app.js @@ -26,6 +26,7 @@ function logScanned(uuid, matches) { status: allMatches[0].fixture ? '' : 'missing', uuid, double: allMatches[0].double, + link: allMatches[0].cellRef, }); } @@ -39,9 +40,10 @@ app.get(['/favicon.ico', '/robots.txt'], (req, res) => { app.get('/search', (req, res) => { loadDatabase((allItems) => { + console.log(allItems); res.render('search', { matches: searchDatabase(req.query, allItems).sort((a, b) => - (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)), + (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)), }); }); }); diff --git a/googleSpreadsheet.js b/googleSpreadsheet.js index f32210d..d481338 100644 --- a/googleSpreadsheet.js +++ b/googleSpreadsheet.js @@ -6,7 +6,7 @@ function rowToObject(val, lab, index) { for (let i = 0; i < lab.length; i += 1) { o[lab[i]] = val[i]; } - o.cellRef = 'https://docs.google.com/spreadsheets/d/1QHKa3vUpht7zRl_LEzl3BlUbolz3ZiL8yKHzdBL42dY/edit#gid=0&range=A' + index; + o.cellRef = 'https://docs.google.com/spreadsheets/d/1QHKa3vUpht7zRl_LEzl3BlUbolz3ZiL8yKHzdBL42dY/edit#gid=0&range=A' + index + ':T' + index; return o; } @@ -22,7 +22,7 @@ function loadDatabase(callback) { return; } return callback(response.values.map((row, index) => - rowToObject(row, response.values[0], index)).splice(1)); + rowToObject(row, response.values[0], index + 1)).splice(1)); }); } diff --git a/views/recent.ejs b/views/recent.ejs index dc1f5bd..e95b2b1 100644 --- a/views/recent.ejs +++ b/views/recent.ejs @@ -4,16 +4,19 @@
    <% for (var i = 0; i < allScans.length; i++) { %>
  • + <%- allScans[i].time.toTimeString() %> - <% if (allScans[i].fixture) { %> - <%- allScans[i].time.toTimeString() %> - <%- allScans[i].fixture %> :
    + <%- allScans[i].fixture %> <% } else { %> - <%- allScans[i].time.toTimeString() %> - Missing Details! :
    + ??? + <% } %> + :
    + <%- allScans[i].uuid %> + <% if (!allScans[i].fixture) { %> + Missing Details! <% } %> - <%- allScans[i].uuid %> <% if (allScans[i].double) { %> - - Duplicate UUID! - + Duplicate UUID! <% } %>
  • <% } %> From 6f7a97f4a32583a1a62f0719eed786c3f2a504ae Mon Sep 17 00:00:00 2001 From: mpsido Date: Wed, 20 Dec 2017 20:45:46 +0800 Subject: [PATCH 09/16] Refactoring/simplifying code googleSpreadsheet.js --- googleSpreadsheet.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/googleSpreadsheet.js b/googleSpreadsheet.js index d481338..f673677 100644 --- a/googleSpreadsheet.js +++ b/googleSpreadsheet.js @@ -1,13 +1,14 @@ const google = require('googleapis'); const keys = require('./config/keys'); -function rowToObject(val, lab, index) { - const o = {}; - for (let i = 0; i < lab.length; i += 1) { - o[lab[i]] = val[i]; +// creates a dictionary mapping column names with the values +// ex: { floor : 402, business : coworking, etc..} +function spreadsheetValuesToObject(values, columns) { + const formatedRow = {}; + for (let i = 0; i < columns.length; i += 1) { + formatedRow[columns[i]] = values[i]; } - o.cellRef = 'https://docs.google.com/spreadsheets/d/1QHKa3vUpht7zRl_LEzl3BlUbolz3ZiL8yKHzdBL42dY/edit#gid=0&range=A' + index + ':T' + index; - return o; + return formatedRow; } function loadDatabase(callback) { @@ -21,8 +22,10 @@ function loadDatabase(callback) { console.log(`The API returned an error: ${err}`); return; } - return callback(response.values.map((row, index) => - rowToObject(row, response.values[0], index + 1)).splice(1)); + const columns = response.values[0]; + // map function transforms a list into another list using the given lambda function + const formatedRows = response.values.map(row => spreadsheetValuesToObject(row, columns)); + return callback(formatedRows); }); } From 1c1d78ab0c3c2c9c4015d39041d1652dd5b8a927 Mon Sep 17 00:00:00 2001 From: mpsido Date: Wed, 20 Dec 2017 21:31:45 +0800 Subject: [PATCH 10/16] Removing hard url in app.js --- app.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 8bd2a6c..7b430bb 100644 --- a/app.js +++ b/app.js @@ -54,7 +54,7 @@ app.get('/qrlist', (req, res) => { .filter(item => item.uuid !== '') .filter(item => item.uuid !== undefined) .sort((a, b) => (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)); - qrList.forEach(item => item.qr = qr.imageSync('http://url.coderbunker.com/' + item.uuid, { type: 'svg' })); + qrList.forEach(item => item.qr = qr.imageSync(item.uuid, { type: 'svg' })); res.render('qrList', { matches: qrList }); }); }); @@ -75,6 +75,9 @@ app.get('/:uuid', (req, res) => { return; } logScanned(req.params.uuid, matches); + if (matches.length >= 1) { + console.log(`Too much matches for uuid ${req.params.uuid} length = ${matches.length}`); + } matches[0].similarItems = searchDatabase({ fixture: matches[0].fixture }, allItems) .filter(item => item.uuid !== matches[0].uuid) .splice(0, 3); From d04c2cf048d633fe6906878e3ffdd1fdeed6af80 Mon Sep 17 00:00:00 2001 From: mpsido Date: Wed, 20 Dec 2017 23:55:13 +0800 Subject: [PATCH 11/16] Refactoring, addRecentlyScanned function instead of logScanned --- app.js | 36 +++++++++++++++++------------------- views/recent.ejs | 18 +++++++++--------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/app.js b/app.js index 7b430bb..11ac8f4 100644 --- a/app.js +++ b/app.js @@ -6,28 +6,26 @@ const { loadDatabase, searchDatabase } = require('./googleSpreadsheet'); const app = express(); -const allScans = []; +const allScans = new Map(); -function logScanned(uuid, matches) { - let allMatches = matches; - const now = new Date(); +function addRecentlyScanned(uuid, item, nbFound = 0) { // TRICK: only record uuids if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(uuid)) { return; } - allScans.map(item => item.status = (item.uuid === uuid) ? 'fixed' : item.status); - if (allMatches.length > 1) { - allMatches = allMatches.splice(0, 1); - allMatches[0].double = true; + + const duplicatedItem = item; + + duplicatedItem.time = new Date(); + duplicatedItem.duplicated = nbFound > 1; + + if (nbFound === 0) { + duplicatedItem.fixture = ''; + duplicatedItem.uuid = uuid; + duplicatedItem.status = 'missing'; } - allScans.unshift({ - time: now, - fixture: allMatches[0].fixture ? allMatches[0].fixture : '', - status: allMatches[0].fixture ? '' : 'missing', - uuid, - double: allMatches[0].double, - link: allMatches[0].cellRef, - }); + + allScans.set(uuid, duplicatedItem); } app.set('view engine', 'ejs'); @@ -67,17 +65,17 @@ app.get('/:uuid', (req, res) => { loadDatabase((allItems) => { const matches = searchDatabase(req.params, allItems); if (matches.length === 0) { - logScanned(req.params.uuid, [{ fixture: null }]); + addRecentlyScanned(req.params.uuid, {}); res.status(404).render('notFound', { item: '', id: req.params.uuid, }); return; } - logScanned(req.params.uuid, matches); - if (matches.length >= 1) { + if (matches.length > 1) { console.log(`Too much matches for uuid ${req.params.uuid} length = ${matches.length}`); } + addRecentlyScanned(req.params.uuid, matches[0], matches.length); matches[0].similarItems = searchDatabase({ fixture: matches[0].fixture }, allItems) .filter(item => item.uuid !== matches[0].uuid) .splice(0, 3); diff --git a/views/recent.ejs b/views/recent.ejs index e95b2b1..3d415a0 100644 --- a/views/recent.ejs +++ b/views/recent.ejs @@ -2,21 +2,21 @@

    Recently scanned QR codes

      - <% for (var i = 0; i < allScans.length; i++) { %> + <% for (let item of allScans.values()) { %>
    • - <%- allScans[i].time.toTimeString() %> - - <% if (allScans[i].fixture) { %> - <%- allScans[i].fixture %> + <%- item.time.toTimeString() %> - + <% if (item.fixture) { %> + <%- item.fixture %> <% } else { %> ??? <% } %> :
      - <%- allScans[i].uuid %> - <% if (!allScans[i].fixture) { %> - Missing Details! + <%- item.uuid %> + <% if (!item.fixture) { %> + Missing Details! <% } %> - <% if (allScans[i].double) { %> - Duplicate UUID! + <% if (item.duplicated) { %> + Duplicate UUID! <% } %>
    • <% } %> From 12229decae992b7c9f8f6182e6e8f8050badc6d9 Mon Sep 17 00:00:00 2001 From: mpsido Date: Fri, 29 Dec 2017 17:14:40 +0800 Subject: [PATCH 12/16] Fixing pre-commit lint warnings, and adding addQrImg function to items --- app.js | 3 +-- googleSpreadsheet.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 19ac732..470d32c 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,5 @@ const express = require('express'); -const qr = require('qr-image'); const { loadDatabase, @@ -57,7 +56,7 @@ app.get('/qrlist', (req, res) => { .filter(item => item.uuid !== '') .filter(item => item.uuid !== undefined) .sort((a, b) => (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)); - qrList.forEach(item => item.qr = qr.imageSync(item.uuid, { type: 'svg' })); + qrList.forEach(item => item.addQrImg()); res.render('qrList', { matches: qrList }); }); }); diff --git a/googleSpreadsheet.js b/googleSpreadsheet.js index 1af81f8..a9a018b 100644 --- a/googleSpreadsheet.js +++ b/googleSpreadsheet.js @@ -1,6 +1,7 @@ const google = require('googleapis'); const keys = require('./config/keys'); const marked = require('marked'); +const qr = require('qr-image'); // creates a dictionary mapping column names with the values // ex: { floor : 402, business : coworking, etc..} @@ -9,6 +10,9 @@ function spreadsheetValuesToObject(values, columns) { for (let i = 0; i < columns.length; i += 1) { formatedRow[columns[i]] = values[i]; } + formatedRow.addQrImg = function addQrImg() { + this.qr = qr.imageSync(this.uuid, { type: 'svg' }); + }; return formatedRow; } @@ -38,14 +42,16 @@ function searchDatabase(query, rows) { return matches; } -function addSimilarItems(obj, allObj) { +function addSimilarItems(refItem, allObj) { + const obj = refItem; obj.similarItems = searchDatabase({ fixture: obj.fixture }, allObj) .filter(item => item.uuid !== obj.uuid) .splice(0, 3); return obj; } -function addMarkdown(obj) { +function addMarkdown(refItem) { + const obj = refItem; obj.HOWTO = marked(obj.HOWTO); obj.details = marked(obj.details); obj.Troubleshooting = marked(obj.Troubleshooting); From 3cd50204412e9a396bcb8a42b59da44a5b206432 Mon Sep 17 00:00:00 2001 From: mpsido Date: Fri, 29 Dec 2017 17:20:30 +0800 Subject: [PATCH 13/16] moving filterEmptyUuid and sortByFloor functions to attribute of matches list --- app.js | 10 ++++------ googleSpreadsheet.js | 6 ++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 470d32c..b656672 100644 --- a/app.js +++ b/app.js @@ -44,18 +44,16 @@ app.get('/search', (req, res) => { loadDatabase((allItems) => { console.log(allItems); res.render('search', { - matches: searchDatabase(req.query, allItems).sort((a, b) => - (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)), + matches: searchDatabase(req.query, allItems).sortByFloor(), }); }); }); app.get('/qrlist', (req, res) => { loadDatabase((allItems) => { - const qrList = searchDatabase(req.query, allItems) - .filter(item => item.uuid !== '') - .filter(item => item.uuid !== undefined) - .sort((a, b) => (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)); + const qrList = searchDatabase(req.query, allItems); + qrList.filterEmptyUuid(); + qrList.sortByFloor(); qrList.forEach(item => item.addQrImg()); res.render('qrList', { matches: qrList }); }); diff --git a/googleSpreadsheet.js b/googleSpreadsheet.js index a9a018b..12a2a95 100644 --- a/googleSpreadsheet.js +++ b/googleSpreadsheet.js @@ -39,6 +39,12 @@ function searchDatabase(query, rows) { Object.keys(query).map((key) => { matches = matches.filter(item => item[key] === query[key]); }); + matches.filterEmptyUuid = function filterEmptyUuid() { + return this.filter(item => item.uuid !== '').filter(item => item.uuid !== undefined); + }; + matches.sortByFloor = function sortByFloor() { + return this.sort((a, b) => (a.floor === b.floor ? 0 : +(a.floor > b.floor) || -1)); + }; return matches; } From d33e8d9844fca6a2c347286496a635cc70e5fb2b Mon Sep 17 00:00:00 2001 From: mpsido Date: Fri, 29 Dec 2017 17:27:24 +0800 Subject: [PATCH 14/16] Removing deprecated similar items function --- app.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/app.js b/app.js index b656672..a63a209 100644 --- a/app.js +++ b/app.js @@ -78,9 +78,6 @@ app.get('/:uuid', (req, res) => { console.log(`Too much matches for uuid ${req.params.uuid} length = ${matches.length}`); } addRecentlyScanned(req.params.uuid, matches[0], matches.length); - matches[0].similarItems = searchDatabase({ fixture: matches[0].fixture }, allItems) - .filter(item => item.uuid !== matches[0].uuid) - .splice(0, 3); addMarkdown(matches[0]); addSimilarItems(matches[0], allItems); res.render('item', matches[0]); From 9b0b9a6330dcb7a8ae1472424bc3ce6145619da7 Mon Sep 17 00:00:00 2001 From: mpsido Date: Fri, 29 Dec 2017 18:22:12 +0800 Subject: [PATCH 15/16] Fixing qrList regression --- app.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index a63a209..66ee113 100644 --- a/app.js +++ b/app.js @@ -42,7 +42,6 @@ app.get(['/favicon.ico', '/robots.txt'], (req, res) => { app.get('/search', (req, res) => { loadDatabase((allItems) => { - console.log(allItems); res.render('search', { matches: searchDatabase(req.query, allItems).sortByFloor(), }); @@ -51,9 +50,9 @@ app.get('/search', (req, res) => { app.get('/qrlist', (req, res) => { loadDatabase((allItems) => { - const qrList = searchDatabase(req.query, allItems); - qrList.filterEmptyUuid(); - qrList.sortByFloor(); + let qrList = searchDatabase(req.query, allItems); + qrList = qrList.sortByFloor(); + qrList = qrList.filterEmptyUuid(); qrList.forEach(item => item.addQrImg()); res.render('qrList', { matches: qrList }); }); From e7731856ee4201441e8ea8ccaf90007c517ca6f0 Mon Sep 17 00:00:00 2001 From: mpsido Date: Fri, 29 Dec 2017 18:23:17 +0800 Subject: [PATCH 16/16] Reintroducing cellRef --- app.js | 2 +- googleSpreadsheet.js | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 66ee113..1c0d904 100644 --- a/app.js +++ b/app.js @@ -22,7 +22,7 @@ function addRecentlyScanned(uuid, item, nbFound = 0) { duplicatedItem.time = new Date(); duplicatedItem.duplicated = nbFound > 1; - + duplicatedItem.link = item.cellRef; if (nbFound === 0) { duplicatedItem.fixture = ''; duplicatedItem.uuid = uuid; diff --git a/googleSpreadsheet.js b/googleSpreadsheet.js index 12a2a95..f83fa88 100644 --- a/googleSpreadsheet.js +++ b/googleSpreadsheet.js @@ -3,16 +3,22 @@ const keys = require('./config/keys'); const marked = require('marked'); const qr = require('qr-image'); +const spreadsheetDataId = '1QHKa3vUpht7zRl_LEzl3BlUbolz3ZiL8yKHzdBL42dY'; +const spreadsheetLink = `https://docs.google.com/spreadsheets/d/${spreadsheetDataId}/edit`; + // creates a dictionary mapping column names with the values // ex: { floor : 402, business : coworking, etc..} -function spreadsheetValuesToObject(values, columns) { +function spreadsheetValuesToObject(values, columns, index) { const formatedRow = {}; for (let i = 0; i < columns.length; i += 1) { formatedRow[columns[i]] = values[i]; } formatedRow.addQrImg = function addQrImg() { - this.qr = qr.imageSync(this.uuid, { type: 'svg' }); + if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(this.uuid)) { + this.qr = qr.imageSync(this.uuid, { type: 'svg' }); + } }; + formatedRow.cellRef = `${spreadsheetLink}#gid=0&range=A${index}:T${index}`; return formatedRow; } @@ -20,7 +26,7 @@ function loadDatabase(callback) { const sheets = google.sheets('v4'); sheets.spreadsheets.values.get({ auth: keys.apiKey, - spreadsheetId: '1QHKa3vUpht7zRl_LEzl3BlUbolz3ZiL8yKHzdBL42dY', + spreadsheetId: spreadsheetDataId, range: 'Agora inventory!A:Z', }, (err, response) => { if (err) { @@ -29,7 +35,11 @@ function loadDatabase(callback) { } const columns = response.values[0]; // map function transforms a list into another list using the given lambda function - const formatedRows = response.values.map(row => spreadsheetValuesToObject(row, columns)); + let i = 0; + const formatedRows = response.values.map((row) => { + i += 1; + return spreadsheetValuesToObject(row, columns, i); + }); return callback(formatedRows); }); }