diff --git a/app/index.js b/app/index.js index d71500b..54d895b 100644 --- a/app/index.js +++ b/app/index.js @@ -5,23 +5,23 @@ if ( process.env.NEW_RELIC_ENABLED ) { const restify = require('restify'); const applyRoutes = require('./routes'); -const logger = require('./lib/logger') -const middleware = require('./lib/middleware') -const package = require('../package') +const logger = require('./lib/logger'); +const middleware = require('./lib/middleware'); +const package = require('../package'); const server = restify.createServer({ name: package.name, version: package.version, - log: logger, + log: logger }); server.pre(restify.pre.sanitizePath()); server.use(restify.acceptParser(server.acceptable)); server.use(restify.queryParser({mapParams: false})); server.use(restify.bodyParser({mapParams: false, rejectUnknown: true})); -server.use(middleware.verifyRequest()) -server.use(middleware.attachResolvePath()) -server.use(middleware.attachErrorLogger()) +server.use(middleware.verifyRequest()); +server.use(middleware.attachResolvePath()); +server.use(middleware.attachErrorLogger()); applyRoutes(server); diff --git a/app/models/badge-instance.js b/app/models/badge-instance.js index db5cce5..908c3c8 100644 --- a/app/models/badge-instance.js +++ b/app/models/badge-instance.js @@ -25,7 +25,7 @@ const BadgeInstances = db.table('badgeInstances', { optional: false }, }, -}) +}); BadgeInstances.formatUserInput = function formatUserInput(obj) { return { @@ -34,14 +34,14 @@ BadgeInstances.formatUserInput = function formatUserInput(obj) { issuedOn: obj.issuedOn || dateFromUnixtime(Date.now()), expires: obj.expires ? dateFromUnixtime(obj.expires) : null, claimCode: obj.claimCode, - } -} + }; +}; BadgeInstances.validateRow = makeValidator({ id: optional('isInt'), email: required('isEmail'), claimCode: optional('len', 0, 255), badgeId: required('isInt'), -}) +}); -exports = module.exports = BadgeInstances +exports = module.exports = BadgeInstances; diff --git a/app/models/badge-type.js b/app/models/badge-type.js new file mode 100644 index 0000000..0a2aac6 --- /dev/null +++ b/app/models/badge-type.js @@ -0,0 +1,47 @@ +const util = require('util'); +const db = require('../lib/db'); +const validation = require('../lib/validation'); + +const makeValidator = validation.makeValidator; +const optional = validation.optional; +const required = validation.required; + +const BadgeTypes = db.table('badgeTypes', { + fields: [ + 'id', + 'name', + 'programId', + 'issuerId', + 'systemId' + ], + relationships: { + system: { + type: 'hasOne', + local: 'systemId', + foreign: { table: 'systems', key: 'id' }, + optional: true, + }, + issuer: { + type: 'hasOne', + local: 'issuerId', + foreign: { table: 'issuers', key: 'id' }, + optional: true, + }, + program: { + type: 'hasOne', + local: 'programId', + foreign: { table: 'programs', key: 'id' }, + optional: true, + } + } +}); + +BadgeTypes.validateRow = makeValidator({ + id: optional('isInt'), + name: required('len', 1, 255), + issuerId: optional('isInt'), + programId: optional('isInt'), + systemId: optional('isInt') +}); + +exports = module.exports = BadgeTypes; diff --git a/app/models/badge.js b/app/models/badge.js index e330266..6a87edf 100644 --- a/app/models/badge.js +++ b/app/models/badge.js @@ -7,8 +7,8 @@ const makeValidator = validation.makeValidator; const optional = validation.optional; const required = validation.required; -const Criteria = require('./criteria') - +const Criteria = require('./criteria'); +const BadgeTypes = require('./badge-type'); const Badges = db.table('badges', { fields: [ 'id', @@ -29,7 +29,8 @@ const Badges = db.table('badges', { 'imageId', 'systemId', 'issuerId', - 'programId' + 'programId', + 'badgeTypeId' ], relationships: { image: { @@ -60,13 +61,19 @@ const Badges = db.table('badges', { type: 'hasMany', local: 'id', foreign: { table: 'criteria', key: 'badgeId' } + }, + badgeType: { + type: 'hasOne', + local: 'badgeTypeId', + foreign: { table: 'badgeTypes', key: 'id' }, + optional: true } }, methods: { setCriteria: setCriteria, del: del, toResponse: function () { - return Badges.toResponse(this) + return Badges.toResponse(this); }, } }); @@ -91,12 +98,14 @@ Badges.toResponse = function toResponse(row) { system: maybeObject(row.system), issuer: maybeObject(row.issuer), program: maybeObject(row.program), + badgeType: maybeObject(row.badgeType), criteriaUrl: row.criteriaUrl, criteria: (row.criteria || []).map(function(criterion) { return Criteria.toResponse(criterion); }) }; } + function maybeObject(obj) { return (obj && obj.id) ? obj.toResponse() : undefined } @@ -117,6 +126,7 @@ Badges.validateRow = makeValidator({ programId: optional('isInt'), issuerId: optional('isInt'), systemId: optional('isInt'), + badgeTypeId: optional('isInt') }); function setCriteria(criteria, callback) { diff --git a/app/routes/applications.js b/app/routes/applications.js index 5bcea7d..f068cd8 100644 --- a/app/routes/applications.js +++ b/app/routes/applications.js @@ -38,7 +38,7 @@ exports = module.exports = function applyApplicationRoutes (server) { middleware.findSystem(), middleware.findIssuer({where: {systemId: ['system', 'id']}}), middleware.findProgram({where: {issuerId: ['issuer', 'id']}}), - middleware.findBadge({where: {programId: ['program', 'id']}}), + middleware.findBadge({where: {programId: ['program', 'id']}}), showAllApplications, ]); function showAllApplications (req, res, next) { @@ -235,5 +235,3 @@ function fromPostToRow (post) { assignedExpiration: post.assignedExpiration }; } - - diff --git a/schema.sql b/schema.sql index 5f11e86..4b81643 100644 --- a/schema.sql +++ b/schema.sql @@ -84,9 +84,23 @@ CREATE TABLE `badges` ( `programId` INT NULL REFERENCES `programs`(`id`), `issuerId` INT NULL REFERENCES `issuers`(`id`), `systemId` INT NULL REFERENCES `systems`(`id`), + `badgeTypeId` INT NULL REFERENCES `badgeTypes`(`id`), UNIQUE KEY `slug_and_system` (`slug`, `systemId`), UNIQUE KEY `slug_and_issuer` (`slug`, `issuerId`), UNIQUE KEY `slug_and_program` (`slug`, `programId`), + UNIQUE KEY `slug_and_badge_type` (`slug`, `badgeTypeId`), + PRIMARY KEY (`id`) +) CHARACTER SET utf8 + ENGINE=InnoDB; + +DROP TABLE IF EXISTS `badgeTypes`; +CREATE TABLE `badgeTypes` ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL UNIQUE, + `slug` VARCHAR(255) NOT NULL UNIQUE, + `programId` INT NULL REFERENCES `programs`(`id`), + `issuerId` INT NULL REFERENCES `issuers`(`id`), + `systemId` INT NULL REFERENCES `systems`(`id`), PRIMARY KEY (`id`) ) CHARACTER SET utf8 ENGINE=InnoDB; diff --git a/script/schema b/script/schema new file mode 100755 index 0000000..2011a39 --- /dev/null +++ b/script/schema @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +if [ -z "$DB_USER" ] || [ -z "$DB_NAME" ]; then + echo "You must have your environment configured for the database" + echo "example:" + echo " export DB_NAME=badgekitapi" + echo " export DB_USER=root" + echo " export DB_PASSWORD=mypassword" +else + if [ -z "$DB_PASSWORD" ]; then + mysql -u $DB_USER $DB_NAME < schema.sql + else + mysql -u $DB_USER "-p$DB_PASSWORD" $DB_NAME < schema.sql + fi +fi diff --git a/script/test b/script/test new file mode 100755 index 0000000..e092a08 --- /dev/null +++ b/script/test @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +source .test-env +if [ -n "$1" ]; then + ./node_modules/.bin/tap test/"$1".test.js +else + npm test +fi +source .env diff --git a/test/badge-type-model.test.js b/test/badge-type-model.test.js new file mode 100644 index 0000000..d83bf03 --- /dev/null +++ b/test/badge-type-model.test.js @@ -0,0 +1,28 @@ +const test = require('tap').test; +const BadgeTypes = require('../app/models/badge-type'); + +const closeDb = BadgeTypes.db.close.bind(BadgeTypes.db); + +test('validating rows', function(t) { + var errors; + + errors = BadgeTypes.validateRow({ + id: 'foo', + name: null, + issuerId: 'foo', + programId: 'foo', + systemId: 'foo' + }); + t.same(errors.length, 5); + + errors = BadgeTypes.validateRow({ + id: 1, + name: 'SomeType', + issuerId: 1, + programId: 1, + systemId: 1 + }); + t.same(errors.length, 0); + + closeDb(); t.end(); +}); diff --git a/test/test-data.sql b/test/test-data.sql index ceed350..d5d12d5 100644 --- a/test/test-data.sql +++ b/test/test-data.sql @@ -65,6 +65,13 @@ INSERT INTO `programs` SET `description` = 'Start learning now', `email` = 'admin@khanacademy.org'; +INSERT INTO `badgeTypes` SET + `systemId` = 1, + `programId` = 1, + `issuerId` = 1, + `slug` = 'community', + `name` = 'Community'; + INSERT INTO `badges` SET `id` = 1, `systemId` = 1,