diff --git a/fixtures/event.js b/fixtures/event.js new file mode 100644 index 0000000..ce9a858 --- /dev/null +++ b/fixtures/event.js @@ -0,0 +1,89 @@ +const faker = require('faker'); + +const capacities = { + high: faker.random.number({ min: 400, max: 600 }), + low: faker.random.number({ min: 125, max: 250 }), +}; + +const dates = { + near: faker.date.future(faker.random.number({ min: 0.125, max: 0.75 })), + past: faker.date.recent(faker.random.number({ min: 3, max: 16 })), +} + +const getPosts = (number = 3) => { + let posts = []; + for (let i = 0; i < number; i++) { + const date = faker.date.recent(faker.random.number({ min: 3, max: 16 })); + const scheduledPost = faker.random.boolean(); + + posts.push({ + author: faker.name.findName(), + title: faker.lorem.sentence(), + content: faker.lorem.paragraphs(faker.random.number({ min: 3, max: 5 })), + isPublic: faker.random.boolean(), + scheduledPost: scheduledPost, + sendNotification: scheduledPost ? faker.date.future(faker.random.number({ min: 0.125, max: 0.25 })) : null, + timestamp: date, + }); + } + + return posts; +}; + +const getTickets = (ticketItems = []) => { + let tickets = []; + + if (ticketItems !== false) { + const count = typeof ticketItems === 'array' ? ticketItems.length : + faker.random.number({ min: 1, max: 5 }); + + for (let i = 0; i < count; i++) { + const ticketItem = ticketItems[i] || {}; + const { + capacity = capacities[faker.random.arrayElement(['high', 'low'])], + itemId = faker.random.uuid(), + name = faker.lorem.sentence(), + price = faker.random.number({ min: 50, max: 450 }), + } = ticketItem; + + tickets.push({ + available: (capacity - + faker.random.number({ min: Math.floor(capacity / 2), max: (capacity - 50) })), + capacity, + endSale: dates.near, + itemId, + name, + price, + startSale: dates.past, + }); + } + } + + return tickets; +} + +const getEvent = (isTicketed = true) => ({ + description: faker.lorem.sentences(faker.random.number({ min: 3, max: 5 }), '. '), + endTime: dates.near, + images: [ + { url: faker.image.imageUrl(640, 480, 'cats', true, true) }, + { url: faker.image.imageUrl(640, 480, 'cats', true, true) }, + { url: faker.image.imageUrl(640, 480, 'cats', true, true) }, + ], + isTicketed: isTicketed, + posts: getPosts(faker.random.number({ min: 4, max: 12 })), + requireLoginToSeeAuction: false, + showFrom: dates.past - (14*24*60*60*1000), + showUntil: dates.near + (14*24*60*60*1000), + startTime: dates.past, + tagline: faker.lorem.sentence(), + ticketClasses: getTickets(isTicketed), + title: faker.lorem.words(faker.random.number({ min: 1, max: 4 })), + url: 'https://www.mfa.org/summer-party', +}); + +module.exports = { + getEvent, + getPosts, + getTickets, +}; diff --git a/fixtures/item.js b/fixtures/item.js index ae8e0b5..364e105 100644 --- a/fixtures/item.js +++ b/fixtures/item.js @@ -1,37 +1,123 @@ -const item = { - eventId: 'x9374bdH93u3ihds453s', - title: 'Stay Local, Get Away!', - subtitle: 'A romantic weekend for two at the Malden Econolodge!', - donor: 'Charlie Baker', - description: 'There may be bedbugs, there is absolutely no continental breakfast, and bring earplugs to sleep through police actions. A luxurious courtyard double.', - images: [ - { url: 'https://random.pics/chdgfj' }, - { url: 'https://random.pics/745gdf' }, - { url: 'https://random.pics/34sd56' }, - ], - type: 'auction', - quantityAvailable: 1, - soldCount: 0, - currentPrice: 125, - startingPrice: 100, - reservePrice: 199, - estimatedValue: 225.49, - currentWinner: 'mifi', - bidders: [ - { name: 'mifi' }, - { name: 'mifi79' }, - ], - bidCount: 2, - bidIncrement: 5, - catalogNumber: 16, - start: Date.now(), - end: Date.now() + (3*60*60*1000), - hideBeforeStart: false, - hideAfterEnd: true, - notifyOnAvailable: false, - isShippable: true, - shippingCost: 25.68, - organizationTake: 0.75, +const faker = require('faker'); + +const item = ({ + donor, + end, + eventId, + index, + quantityAvailable, + soldCount, + start, + startPrice, + subtitle, + title, + type, +}) => { + const isAuction = type === 'auction'; + const startingPrice = startPrice || faker.random.number({ min: 100, max: 500 }); + const bidCount = isAuction ? faker.random.number({ min: 0, max: 5 }) : 0; + const bidIncrement = isAuction ? faker.random.number({ min: 5, max: 100 }) : null; + + currentPrice = !isAuction ? null : startPrice; + currentPrice = bidCount > 0 + ? currentPrice + faker.random.number({ min: (bidIncrement * bidCount), max: (bidIncrement * bidCount * 4) }) + : currentPrice; + + const estimatedValue = isAuction ? + (startingPrice * faker.random.number({ min: 15, max: 20 })) : 0; + const reservePrice = isAuction && faker.random.boolean() ? + (startingPrice * faker.random.number({ min: 5, max: 10 })) : 0; + + const isShippable = faker.random.boolean(); + + return { + eventId: eventId || faker.random.uuid(), + title: title || faker.lorem.sentence(), + subtitle: subtitle === false ? null : subtitle || faker.lorem.sentence(), + donor: donor === false ? null : donor || faker.name.findName(), + description: faker.lorem.sentences(faker.random.number({ min: 2, max: 5 }, '. ')), + images: type === 'ticket' ? null : [ + { url: faker.image.imageUrl(640, 480, 'cats', true, true) }, + { url: faker.image.imageUrl(640, 480, 'cats', true, true) }, + { url: faker.image.imageUrl(640, 480, 'cats', true, true) }, + ], + type: type || faker.random.arrayElement(), + quantityAvailable: quantityAvailable || 1, + soldCount: soldCount || 0, + currentPrice: currentPrice === false ? null : faker.random.number({ }), + startingPrice, + reservePrice, + estimatedValue, + currentWinner: null, + bidders: [], + bidCount, + bidIncrement, + catalogNumber: index || null, + start, + end: end || start + (3*60*60*1000), + hideBeforeStart: faker.random.boolean(), + hideAfterEnd: faker.random.boolean(), + notifyOnAvailable: faker.random.boolean(), + isShippable, + shippingCost: isShippable ? faker.random.number({ min: 5.5, max: 39.98 }) : null, + organizationTake: isAuction ? faker.random.number({ min: 0.25, max: 1 }) : 1, + }; }; -module.exports = item; +const getItems = ({ + eventId = faker.random.uuid(), + auction = 10, + ticket = 4, + eventStart = Date.now(), + eventEnd = (Date.now() + (3*60*60*1000)), +}) => { + let items = []; + + if (typeof ticket === 'number') { + for (let i = 0; i < ticket; i++) { + items.push(item({ + donor: false, + end: eventEnd, + eventId, + index: false, + start: eventStart, + subtitle: false, + type: 'ticket', + })); + } + } + + if (typeof ticket === 'object' && ticket.length) { + for (let i = 0; i < ticket.length; i++) { + items.push(item({ + donor: false, + end: ticket[i].endSale, + eventId, + index: false, + quantityAvailable: ticket[i].capacity, + soldCount: (ticket[i].capacity - ticket[i].available), + start: ticket[i].startSale, + startPrice: ticket[i].price, + subtitle: false, + type: 'ticket', + title: ticket[i].name, + })); + } + } + + if (typeof auction === 'number') { + for (let i = 0; i < auction; i++) { + items.push(item({ + end: eventEnd, + eventId, + index: i, + start: eventStart, + type: 'auction', + })); + } + } + + return items; +}; + +module.exports = getItems; diff --git a/models/event.js b/models/event.js index 99b49d4..bf29779 100644 --- a/models/event.js +++ b/models/event.js @@ -20,14 +20,16 @@ const PostSchema = new mongoose.Schema( default: true, }, - scheduledPost: String, - - sendNotification: { + scheduledPost: { type: Boolean, default: false, }, + + sendNotification: Date, notificationContent: String, notificationLinksTo: String, + + timestamp: Date, } ); @@ -59,14 +61,12 @@ const TicketSchema = new mongoose.Schema( }, startSale: { - type: String, + type: Date, required: true, - trim: true, }, endSale: { - type: String, + type: Date, required: true, - trim: true, }, }, @@ -75,18 +75,12 @@ const TicketSchema = new mongoose.Schema( const EventSchema = new mongoose.Schema( { - eventDate: { - type: String, - required: true, - trim: true, - }, startTime: { - type: String, + type: Date, required: true, - trim: true, }, endTime: { - type: Number, + type: Date, required: true, }, @@ -113,14 +107,12 @@ const EventSchema = new mongoose.Schema( ticketClasses: [ TicketSchema ], showFrom: { - type: String, + type: Date, required: true, - trim: true, }, showUntil: { - type: String, + type: Date, required: true, - trim: true, }, requireLoginToSeeAuction: { diff --git a/models/item.js b/models/item.js index 63434ac..b58e4f3 100644 --- a/models/item.js +++ b/models/item.js @@ -81,11 +81,11 @@ const ItemSchema = new mongoose.Schema( }, start: { - type: Number, + type: Date, required: true, }, end: { - type: Number, + type: Date, required: true, }, hideBeforeStart: { diff --git a/package.json b/package.json index 5b5026f..06bae47 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "dependencies": { "api-query-params": "^4.13.0", "crypto": "^1.0.1", + "faker": "^4.1.0", "jsonwebtoken": "^8.5.1", "mongoose": "^5.6.0", "mongoose-timestamp": "^0.6.0", diff --git a/routes/auth.js b/routes/auth.js index 701c610..dc3ae2f 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -90,49 +90,49 @@ module.exports = function (server, auth) { } ); - server.get( - '/auth/facebook/link', - auth.secure, - (req, res, next) => { - req.user.record.setLinkCheckBit((err, linkCheckBit) => { - passport.authenticate('facebookLink', { - scope: ['email', 'public_profile'], - session: false, - state: linkCheckbit, - })(req, res, next), - }); - }, - ); - - server.get( - '/auth/facebook/linked', - (req, res, next) => { - const linkCheckBit = req.query.state; - - return passport.authenticate( - 'facebook', - { failureRedirect: '/profile' }, - (err, profile) => { - if (err) { - return next(err); - } - - User.linkFacebookProfile(linkCheckBit, profile, (err, user) => { - if (err) { - return next(err); - } - - if (!user) { - return next(err, false, 'Linking the account to Facebook was unsuccessful, please try again.'); - } - - res.send({ - success: true, - info: 'Facerbook account successfully linked', - }); - }); - }, - )(req, res, next); - } - ); +// server.get( +// '/auth/facebook/link', +// auth.secure, +// (req, res, next) => { +// req.user.record.setLinkCheckBit((err, linkCheckBit) => { +// passport.authenticate('facebookLink', { +// scope: ['email', 'public_profile'], +// session: false, +// state: linkCheckbit, +// })(req, res, next); +// }); +// }, +// ); +// +// server.get( +// '/auth/facebook/linked', +// (req, res, next) => { +// const linkCheckBit = req.query.state; +// +// return passport.authenticate( +// 'facebook', +// { failureRedirect: '/profile' }, +// (err, profile) => { +// if (err) { +// return next(err); +// } +// +// User.linkFacebookProfile(linkCheckBit, profile, (err, user) => { +// if (err) { +// return next(err); +// } +// +// if (!user) { +// return next(err, false, 'Linking the account to Facebook was unsuccessful, please try again.'); +// } +// +// res.send({ +// success: true, +// info: 'Facerbook account successfully linked', +// }); +// }); +// }, +// )(req, res, next); +// } +// ); }; diff --git a/routes/demo.js b/routes/demo.js new file mode 100644 index 0000000..b165eb5 --- /dev/null +++ b/routes/demo.js @@ -0,0 +1,102 @@ +const aqp = require('api-query-params'); +const errors = require('restify-errors'); + +const EventFixture = require('../fixtures/event'); +const ItemFixture = require('../fixtures/item'); + +const Event = require('../models/event'); +const Item = require('../models/item'); + +const mergeItemTicketsWithEventStubTickets = (items, ticketStubs, callback) => { + const getMatchingTicket = (doc) => { + for (let i = 0; i < ticketStubs.length; i++) { + if (doc.title === ticketStubs[i].name) { + ticketStubs[i].itemId = doc.id; + return ticketStubs[i]; + } + } + + return null; + }; + + let ticketsWithItemIds = []; + items.forEach((item) => { + if (item.type === 'ticket') { + const mergedTicket = getMatchingTicket(item); + if (mergedTicket) { + ticketsWithItemIds.push(mergedTicket); + } + } + }); + + callback(ticketsWithItemIds); +}; + +module.exports = function (server, auth) { + + server.get('/demo/populate', auth.manager, (req, res, next) => { + const eventStub = EventFixture.getEvent(true); + const eventTickets = eventStub.ticketClasses; + delete eventStub.ticketClasses; + + Event.create(eventStub, (err, event) => { + if (err) { + return next(err); + } + + if (!event) { + return next(err, false, 'There was a problem creating the event'); + } + + const itemsStub = ItemFixture({ + eventId: event.id, + auction: 10, + ticket: eventTickets, + eventStart: eventStub.startTime, + eventEnd: eventStub.endTime, + }); + + Item.insertMany(itemsStub, (err, items) => { + if (err) { + return next(err); + } + + if (!items.length) { + return next(err, false, 'No items were inserted.'); + } + + + + mergeItemTicketsWithEventStubTickets(items, eventTickets, (ticketsWithIds) => { + Event.updateOne( + { _id: event.id }, + { $set: { ticketed: true, ticketClasses: ticketsWithIds } }, + (err, event) => { + if (err) { + return next(err); + } + + res.send({ success: true, info: 'Demo data successfully inserted.' }); + next(); + }, + ); + }); + }); + }); + }); + + server.get('/demo/depopulate', auth.manager, (req, res, next) => { + let report = { items: null, events: null }; + + Event.deleteMany({}, (err, { deletedCount = 0 }) => { + report.events = deletedCount; + Item.deleteMany({}, (err, { deletedCount = 0 }) => { + report.items = deletedCount; + res.send({ report }); + return next(); + }); + }); + + next(); + }); +}; diff --git a/routes/events.js b/routes/events.js index 189e042..2b07b5f 100644 --- a/routes/events.js +++ b/routes/events.js @@ -20,7 +20,7 @@ module.exports = function (server, auth) { }); server.get('/events', auth.basic, (req, res, next) => { - Event.apiQuery(req.params, function(err, docs) { + Event.find(req.params, function(err, docs) { if (err) { console.error(err); return next( diff --git a/routes/index.js b/routes/index.js index b94ca5f..b62f701 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,6 +1,7 @@ module.exports = function(server, auth) { require('./auth.js')(server, auth); require('./bids.js')(server, auth); + require('./demo.js')(server, auth); require('./events.js')(server, auth); require('./installs.js')(server, auth); require('./items.js')(server, auth); diff --git a/yarn.lock b/yarn.lock index 034ec4c..da51bb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -303,6 +303,11 @@ extsprintf@^1.2.0, extsprintf@^1.4.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= +faker@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f" + integrity sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8= + fast-decode-uri-component@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543"