From 5ac78a5edf5f70ea2e7369d03fb0edfc1c327f0a Mon Sep 17 00:00:00 2001 From: Gian Franco Zabarino Date: Fri, 7 Nov 2014 19:12:27 -0300 Subject: [PATCH 1/2] - Added lookupModel property in most controller calls to allow using a ManyToMany model. - Added tables creation and migration scripts. - Fixed swaggerTestHelper to allow definitions without a required setting. --- api/controllers/challenges.js | 9 +- api/controllers/files.js | 5 +- api/controllers/requirements.js | 2 +- api/controllers/scorecardItems.js | 2 +- api/controllers/scorecards.js | 2 +- api/models/challenge.js | 2 +- api/models/challengeFile.js | 39 ++++++ api/models/file.js | 17 +-- api/models/submission.js | 2 +- api/models/submissionFile.js | 38 ++++++ .../20141010160840-tables.js | 4 +- .../20141106235712-files-tables.js | 54 ++++++++ .../20141107002604-files-migrate.js | 37 +++++ lib/controllerHelper.js | 128 +++++++++++++----- test/controllers/challenges.js | 1 - test/controllers/files.js | 1 - test/swaggerTestHelper.js | 2 +- 17 files changed, 280 insertions(+), 65 deletions(-) create mode 100644 api/models/challengeFile.js create mode 100644 api/models/submissionFile.js create mode 100644 config/schema-migrations/20141106235712-files-tables.js create mode 100644 config/schema-migrations/20141107002604-files-migrate.js diff --git a/api/controllers/challenges.js b/api/controllers/challenges.js index 1fca392..eff46ba 100644 --- a/api/controllers/challenges.js +++ b/api/controllers/challenges.js @@ -12,6 +12,7 @@ var datasource = require('./../../datasource').getDataSource(); var Challenge = datasource.Challenge; +var ChallengeFile = datasource.ChallengeFile; var File = datasource.File; var Participant = datasource.Participant; var Submission = datasource.Submission; @@ -20,7 +21,7 @@ var controllerHelper = require('./../../lib/controllerHelper'); // build controller for challenge resource -var challengeController = controllerHelper.buildController(Challenge, null, {filtering: true}); +var challengeController = controllerHelper.buildController(Challenge, null, null, {filtering: true}); var filteringOff = { @@ -28,13 +29,13 @@ var filteringOff = { }; // build controller for the nested files resource -var fileController = controllerHelper.buildController(File, [Challenge], filteringOff); +var fileController = controllerHelper.buildController(File, [Challenge], ChallengeFile, filteringOff); // build controller for the nested participants resource -var participantController = controllerHelper.buildController(Participant, [Challenge], {filtering: true}); +var participantController = controllerHelper.buildController(Participant, [Challenge], null, {filtering: true}); // build controller for the nested submissions resource -var submissionController = controllerHelper.buildController(Submission, [Challenge], filteringOff); +var submissionController = controllerHelper.buildController(Submission, [Challenge], null, filteringOff); diff --git a/api/controllers/files.js b/api/controllers/files.js index e0762cc..cf57b8b 100644 --- a/api/controllers/files.js +++ b/api/controllers/files.js @@ -11,15 +11,14 @@ var datasource = require('./../../datasource').getDataSource(); -var Challenge = datasource.Challenge; var Submission = datasource.Submission; +var SubmissionFile = datasource.SubmissionFile; var File = datasource.File; var controllerHelper = require('./../../lib/controllerHelper'); // build controller for the nested files resource -var fileController = controllerHelper.buildController(File, [Challenge, Submission], {filtering: false}); - +var fileController = controllerHelper.buildController(File, [Submission], SubmissionFile, {filtering: false}); module.exports = { getFiles: fileController.all, diff --git a/api/controllers/requirements.js b/api/controllers/requirements.js index 248aeef..a56e31c 100644 --- a/api/controllers/requirements.js +++ b/api/controllers/requirements.js @@ -17,7 +17,7 @@ var controllerHelper = require('./../../lib/controllerHelper'); // build controller for the nested files resource -var requirementController = controllerHelper.buildController(Requirement, [Challenge], {filtering: false}); +var requirementController = controllerHelper.buildController(Requirement, [Challenge], null, {filtering: false}); module.exports = { diff --git a/api/controllers/scorecardItems.js b/api/controllers/scorecardItems.js index 42cbdec..32cacf2 100644 --- a/api/controllers/scorecardItems.js +++ b/api/controllers/scorecardItems.js @@ -24,7 +24,7 @@ var options = { // build controller for the nested scorecardItems resource -var scorecardItemController = controllerHelper.buildController(ScorecardItem, [Challenge, Scorecard], options); +var scorecardItemController = controllerHelper.buildController(ScorecardItem, [Challenge, Scorecard], null, options); module.exports = { diff --git a/api/controllers/scorecards.js b/api/controllers/scorecards.js index d618865..e43dc73 100644 --- a/api/controllers/scorecards.js +++ b/api/controllers/scorecards.js @@ -17,7 +17,7 @@ var controllerHelper = require('./../../lib/controllerHelper'); // build controller for the nested scorecards resource -var scorecardController = controllerHelper.buildController(Scorecard, [Challenge], {filtering: false}); +var scorecardController = controllerHelper.buildController(Scorecard, [Challenge], null, {filtering: false}); module.exports = { diff --git a/api/models/challenge.js b/api/models/challenge.js index 2046488..23b2610 100644 --- a/api/models/challenge.js +++ b/api/models/challenge.js @@ -55,7 +55,7 @@ module.exports = function(sequelize, DataTypes) { }, { tableName : 'challenges', associate : function(models) { - Challenge.hasMany(models.File); + Challenge.hasMany(models.ChallengeFile); Challenge.hasMany(models.Participant); Challenge.hasMany(models.Submission); Challenge.hasMany(models.Scorecard); diff --git a/api/models/challengeFile.js b/api/models/challengeFile.js new file mode 100644 index 0000000..dbfa1b4 --- /dev/null +++ b/api/models/challengeFile.js @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2014 TopCoder, Inc. All rights reserved. + */ +/** + * Represent File in the system. + * + * @author gfzabarino + */ +'use strict'; + +/** + * Defining ChallengeFile model + */ +module.exports = function(sequelize, DataTypes) { + var ChallengeFile = sequelize.define('ChallengeFile', { + challengeId: { + type: DataTypes.BIGINT, primaryKey: true, + get: function() { + return parseInt(this.getDataValue('challengeId')); + } + }, + fileId: { + type: DataTypes.BIGINT, primaryKey: true, + get: function() { + return parseInt(this.getDataValue('fileId')); + } + }, + + }, { + tableName : 'challenge_files', + associate : function(models) { + ChallengeFile.belongsTo(models.Challenge, {foreignKey : 'challengeId'}); + ChallengeFile.belongsTo(models.File, {foreignKey : 'fileId'}); + } + }); + + return ChallengeFile; + +}; diff --git a/api/models/file.js b/api/models/file.js index 8405282..80c6bb8 100644 --- a/api/models/file.js +++ b/api/models/file.js @@ -21,18 +21,6 @@ module.exports = function(sequelize, DataTypes) { return parseInt(this.getDataValue('id')); } }, - challengeId: { - type: DataTypes.BIGINT, allowNull: false, - get: function() { - return parseInt(this.getDataValue('challengeId')); - } - }, - submissionId: { - type: DataTypes.BIGINT, allowNull: false, - get: function() { - return parseInt(this.getDataValue('submissionId')); - } - }, title : {type: DataTypes.TEXT}, filePath : {type: DataTypes.TEXT, allowNull: false}, size : { @@ -54,9 +42,8 @@ module.exports = function(sequelize, DataTypes) { }, { tableName : 'files', associate : function(models) { - File.belongsTo(models.Challenge, {foreignKey : 'challengeId'}); - File.belongsTo(models.Submission, {foreignKey : 'submissionId'}); - + File.hasMany(models.ChallengeFile); + File.hasMany(models.SubmissionFile); } }); diff --git a/api/models/submission.js b/api/models/submission.js index 05cb2f5..0671d09 100644 --- a/api/models/submission.js +++ b/api/models/submission.js @@ -39,7 +39,7 @@ module.exports = function(sequelize, DataTypes) { }, { tableName : 'submissions', associate : function(models) { - Submission.hasMany(models.File); + Submission.hasMany(models.SubmissionFile); Submission.hasMany(models.Scorecard); Submission.belongsTo(models.User, {foreignKey : 'submitterId'}); Submission.belongsTo(models.Challenge, {foreignKey : 'challengeId'}); diff --git a/api/models/submissionFile.js b/api/models/submissionFile.js new file mode 100644 index 0000000..a98252a --- /dev/null +++ b/api/models/submissionFile.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2014 TopCoder, Inc. All rights reserved. + */ +/** + * Represent File in the system. + * + * @author gfzabarino + */ +'use strict'; + +/** + * Defining ChallengeFile model + */ +module.exports = function(sequelize, DataTypes) { + var SubmissionFile = sequelize.define('SubmissionFile', { + submissionId: { + type: DataTypes.BIGINT, primaryKey: true, + get: function() { + return parseInt(this.getDataValue('submissionId')); + } + }, + fileId: { + type: DataTypes.BIGINT, primaryKey: true, + get: function() { + return parseInt(this.getDataValue('fileId')); + } + } + }, { + tableName : 'submission_files', + associate : function(models) { + SubmissionFile.belongsTo(models.Submission, {foreignKey : 'submissionId'}); + SubmissionFile.belongsTo(models.File, {foreignKey : 'fileId'}); + } + }); + + return SubmissionFile; + +}; diff --git a/config/schema-migrations/20141010160840-tables.js b/config/schema-migrations/20141010160840-tables.js index 34c634d..e1ed44a 100644 --- a/config/schema-migrations/20141010160840-tables.js +++ b/config/schema-migrations/20141010160840-tables.js @@ -40,9 +40,7 @@ exports.up = function (db, callback) { '"createdAt" timestamp with time zone NOT NULL, ' + '"updatedAt" timestamp with time zone NOT NULL, ' + '"createdBy" character varying(128), ' + - '"updatedBy" character varying(128), ' + - '"submissionId" bigint, ' + - '"challengeId" bigint NOT NULL ' + + '"updatedBy" character varying(128) ' + ');'), db.runSql.bind(db, 'ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (id);'), diff --git a/config/schema-migrations/20141106235712-files-tables.js b/config/schema-migrations/20141106235712-files-tables.js new file mode 100644 index 0000000..ac72fa3 --- /dev/null +++ b/config/schema-migrations/20141106235712-files-tables.js @@ -0,0 +1,54 @@ +var dbm = require('db-migrate'); +var type = dbm.dataType; +var async = require('async'); + +exports.up = function(db, callback) { + + async.series([ + function (callback) { + // challenge files table + db.createTable('challenge_files', { + columns: { + '"fileId"': {type: 'int', primaryKey: true}, + '"challengeId"': {type: 'int', primaryKey: true} + }, + ifNotExists: true + }, callback); + }, function (callback) { + // submission files table + db.createTable('submission_files', { + columns: { + '"fileId"': {type: 'int', primaryKey: true}, + '"submissionId"': {type: 'int', primaryKey: true} + }, + ifNotExists: true + }, callback); + }, function (callback) { + db.runSql('ALTER TABLE challenge_files ADD CONSTRAINT challenge_fk FOREIGN KEY ("challengeId") REFERENCES challenges (id) ON UPDATE CASCADE ON DELETE CASCADE;', callback); + }, function (callback) { + db.runSql('ALTER TABLE submission_files ADD CONSTRAINT submission_fk FOREIGN KEY ("submissionId") REFERENCES submissions (id) ON UPDATE CASCADE ON DELETE CASCADE;', callback); + }, function (callback) { + db.runSql('ALTER TABLE challenge_files ADD CONSTRAINT file_fk FOREIGN KEY ("fileId") REFERENCES files (id) ON UPDATE CASCADE ON DELETE CASCADE;', callback); + }, function (callback) { + db.runSql('ALTER TABLE submission_files ADD CONSTRAINT file_fk FOREIGN KEY ("fileId") REFERENCES files (id) ON UPDATE CASCADE ON DELETE CASCADE;', callback); + }, function (callback) { + db.runSql('ALTER TABLE challenge_files ADD COLUMN "createdAt" timestamp with time zone NOT NULL;', callback); + }, function (callback) { + db.runSql('ALTER TABLE challenge_files ADD COLUMN "updatedAt" timestamp with time zone NOT NULL;', callback); + }, function (callback) { + db.runSql('ALTER TABLE submission_files ADD COLUMN "createdAt" timestamp with time zone NOT NULL;', callback); + }, function (callback) { + db.runSql('ALTER TABLE submission_files ADD COLUMN "updatedAt" timestamp with time zone NOT NULL;', callback); + } + ], callback); +}; + +exports.down = function(db, callback) { + async.series([ + function (callback) { + db.dropTable('challenge_files', callback); + }, function (callback) { + db.dropTable('submission_files', callback); + } + ], callback); +}; diff --git a/config/schema-migrations/20141107002604-files-migrate.js b/config/schema-migrations/20141107002604-files-migrate.js new file mode 100644 index 0000000..efa81bf --- /dev/null +++ b/config/schema-migrations/20141107002604-files-migrate.js @@ -0,0 +1,37 @@ +var dbm = require('db-migrate'); +var async = require('async'); +var type = dbm.dataType; + +exports.up = function(db, callback) { + console.info('migrate!!!'); + db.all('SELECT "column_name" ' + + 'FROM "information_schema"."columns" ' + + 'WHERE "table_name"=\'files\' and "column_name"=\'challengeId\';', function (err, results) { + if (err) { + return callback(err); + } + if (results && results.length) { + async.series([ + function (callback) { + db.runSql('INSERT INTO challenge_files ("fileId", "challengeId", "createdAt", "updatedAt") ' + + 'SELECT id, "challengeId", "createdAt", "updatedAt" ' + + 'FROM files WHERE "challengeId" IS NOT NULL', callback); + }, function (callback) { + db.runSql('INSERT INTO submission_files ("fileId", "submissionId", "createdAt", "updatedAt") ' + + 'SELECT id, "submissionId", "createdAt", "updatedAt" ' + + 'FROM files WHERE "submissionId" IS NOT NULL', callback); + }, function (callback) { + db.runSql('ALTER TABLE files DROP COLUMN "submissionId"', callback); + }, function (callback) { + db.runSql('ALTER TABLE files DROP COLUMN "challengeId"', callback); + } + ], callback); + } else { + callback(); + } + }); +}; + +exports.down = function(db, callback) { + callback(); +}; diff --git a/lib/controllerHelper.js b/lib/controllerHelper.js index 2e79f1e..df858c0 100644 --- a/lib/controllerHelper.js +++ b/lib/controllerHelper.js @@ -19,11 +19,12 @@ var paramHelper = require('./paramHelper'); /** * Find an entity with the provided filters and its own id. * @param model the entity model + * @param lookupModel the model to lookup when filtering by reference models * @param filters the current filters * @param req the request * @param callback the async callback */ -function _findEntityByFilter(model, filters, req, callback) { +function _findEntityByFilter(model, lookupModel, filters, req, callback) { // add the id parameter to the filters var idParam = routeHelper.getRefIdField(model); var idParamValue = req.swagger.params[idParam].value; @@ -33,20 +34,42 @@ function _findEntityByFilter(model, filters, req, callback) { routeHelper.addErrorMessage(req, 'Invalid id parameter '+idParamValue, 400); callback(req.error); } else { - var refFilters = _.cloneDeep(filters); - refFilters.where.id = idValue; - model.find(refFilters).success(function(entity) { - if (!entity) { - routeHelper.addErrorMessage(req, 'Cannot find the '+ model.name + ' with id '+idParamValue, 404); + var findById = function (idValue) { + var refFilters = _.cloneDeep(filters); + refFilters.where.id = idValue; + model.find(refFilters).success(function(entity) { + if (!entity) { + routeHelper.addErrorMessage(req, 'Cannot find the '+ model.name + ' with id '+idParamValue, 404); + callback(req.error); + } else { + callback(null, filters, entity); + } + }) + .error(function(err) { + routeHelper.addError(req, err); callback(req.error); - } else { - callback(null, filters, entity); - } - }) - .error(function(err) { - routeHelper.addError(req, err); - callback(req.error); - }); + }); + }; + if (lookupModel) { + var query = {}; + lookupModel.primaryKeyAttributes.forEach(function (primaryKey) { + query[primaryKey] = req.swagger.params[primaryKey].value; + if (primaryKey in filters.where) { + delete filters.where[primaryKey]; + } + }); + // validate the entity belongs to the associated lookupModel + lookupModel.find(query).success(function() { + findById(idValue); + }) + .error(function(err) { + routeHelper.addError(req, err); + callback(req.error); + }); + console.info(''); + } else { + findById(idValue); + } } } @@ -150,12 +173,13 @@ function _checkExtraParameters(req, callback) { and search criterias if filtering is enabled. * @param model the entity model * @param referenceModels the array of referencing models + * @param lookupModel the model to lookup when filtering by reference models * @param options the controller options * @param req the request * @param res the response * @param next the next function in the chain */ -function getAllEntities(model, referenceModels, options, req, res, next) { +function getAllEntities(model, referenceModels, lookupModel, options, req, res, next) { async.waterfall([ function(callback) { if (!options.filtering) { @@ -177,8 +201,29 @@ function getAllEntities(model, referenceModels, options, req, res, next) { return options.entityFilterIDs.indexOf(key) === -1; }); } - + if (lookupModel) { + var referenceIdField = routeHelper.getRefIdField(referenceModels[0]); + if (referenceIdField in filters.where) { + filters.where[lookupModel.tableName + '.' + referenceIdField] = filters.where[referenceIdField]; + delete filters.where[referenceIdField]; + } + filters.include = [ lookupModel ]; + } model.findAndCountAll(filters).success(function(result) { + if (lookupModel) { + var name = lookupModel.name + 's'; + var keyToDelete = name.charAt(0).toLowerCase() + name.slice(1); + result.rows.forEach(function (row) { + if (keyToDelete in row) { + delete row[keyToDelete]; + } + ['dataValues', '_previousDataValues'].forEach(function (rowKey) { + if (keyToDelete in row[rowKey]) { + delete row[rowKey][keyToDelete]; + } + }); + }); + } callback(null, result.count, result.rows); }) .error(function(err) { @@ -206,11 +251,12 @@ function getAllEntities(model, referenceModels, options, req, res, next) { * This function gets an entity by id. * @param model the entity model * @param referenceModels the array of referencing models + * @param lookupModel the model to lookup when filtering by reference models * @param options the controller options * @param res the response * @param next the next function in the chain */ -function getEntity(model, referenceModels, options, req, res, next) { +function getEntity(model, referenceModels, lookupModel, options, req, res, next) { async.waterfall([ function(callback) { _checkExtraParameters(req, callback); @@ -225,7 +271,7 @@ function getEntity(model, referenceModels, options, req, res, next) { return options.entityFilterIDs.indexOf(key) === -1; }); } - _findEntityByFilter(model, filters, req, callback); + _findEntityByFilter(model, lookupModel, filters, req, callback); } ], function(err, filters, entity) { if (!err) { @@ -244,12 +290,13 @@ function getEntity(model, referenceModels, options, req, res, next) { * This function creates an entity. * @param model the entity model * @param referenceModels the array of referencing models + * @param lookupModel the model to lookup when filtering by reference models * @param options the controller options * @param req the request * @param res the response * @param next the next function in the chain */ -function createEntity(model, referenceModels, options, req, res, next) { +function createEntity(model, referenceModels, lookupModel, options, req, res, next) { async.waterfall([ function(callback) { _checkExtraParameters(req, callback); @@ -263,10 +310,25 @@ function createEntity(model, referenceModels, options, req, res, next) { // set createdBy and updatedBy user data.createdBy = routeHelper.getSigninUser(); data.updatedBy = routeHelper.getSigninUser(); - // add foreign keys - _.extend(data, filters.where); + if (!lookupModel) { + // add foreign keys + _.extend(data, filters.where); + } model.create(data).success(function(entity) { - callback(null, entity); + if (lookupModel) { + var lookupData = {}; + _.extend(lookupData, filters.where); + lookupData[routeHelper.getRefIdField(model)] = entity.id; + lookupModel.create(lookupData).success(function() { + callback(null, entity); + }) + .error(function(err) { + routeHelper.addError(req, err); + callback(req.error); + }); + } else { + callback(null, entity); + } }) .error(function(err) { routeHelper.addError(req, err); @@ -291,12 +353,13 @@ function createEntity(model, referenceModels, options, req, res, next) { * This function updates an entity. * @param model the entity model * @param referenceModels the array of referencing models + * @param lookupModel the model to lookup when filtering by reference models * @param options the controller options * @param req the request * @param res the response * @param next the next function in the chain */ -function updateEntity(model, referenceModels, options, req, res, next) { +function updateEntity(model, referenceModels, lookupModel, options, req, res, next) { async.waterfall([ function(callback) { _checkExtraParameters(req, callback); @@ -311,7 +374,7 @@ function updateEntity(model, referenceModels, options, req, res, next) { return options.entityFilterIDs.indexOf(key) === -1; }); } - _findEntityByFilter(model, filters, req, callback); + _findEntityByFilter(model, lookupModel, filters, req, callback); }, function(filters, entity, callback) { var excludeFields = Object.keys(filters.where); @@ -355,12 +418,13 @@ function updateEntity(model, referenceModels, options, req, res, next) { * This function deletes an entity. * @param model the entity model * @param referenceModels the array of referencing models + * @param lookupModel the model to lookup when filtering by reference models * @param options the controller options * @param req the request * @param res the response * @param next the next function in the chain */ -function deleteEntity(model, referenceModels, options, req, res, next) { +function deleteEntity(model, referenceModels, lookupModel, options, req, res, next) { async.waterfall([ function(callback) { _checkExtraParameters(req, callback); @@ -375,7 +439,7 @@ function deleteEntity(model, referenceModels, options, req, res, next) { return options.entityFilterIDs.indexOf(key) === -1; }); } - _findEntityByFilter(model, filters, req, callback); + _findEntityByFilter(model, lookupModel, filters, req, callback); }, function(filters, entity, callback) { entity.destroy().success(function() { @@ -404,32 +468,32 @@ function deleteEntity(model, referenceModels, options, req, res, next) { /** * Build the CRUD controller for a model. */ -exports.buildController = function(model, referenceModels, options) { +exports.buildController = function(model, referenceModels, lookupModel, options) { var controller = {}; // Get an entity. controller.get = function(req, res, next) { - getEntity(model, referenceModels, options, req, res, next); + getEntity(model, referenceModels, lookupModel, options, req, res, next); }; // Create an entity. controller.create = function(req, res, next) { - createEntity(model, referenceModels, options, req, res, next); + createEntity(model, referenceModels, lookupModel, options, req, res, next); }; // Update an entity. controller.update = function(req, res, next) { - updateEntity(model, referenceModels, options, req, res, next); + updateEntity(model, referenceModels, lookupModel, options, req, res, next); }; // Retrieve all entities. controller.all = function(req, res, next) { - getAllEntities(model, referenceModels, options, req, res, next); + getAllEntities(model, referenceModels, lookupModel, options, req, res, next); }; // Delete an entity. controller.delete = function(req, res, next) { - deleteEntity(model, referenceModels, options, req, res, next); + deleteEntity(model, referenceModels, lookupModel, options, req, res, next); }; return controller; diff --git a/test/controllers/challenges.js b/test/controllers/challenges.js index 39fe39b..e5160b3 100644 --- a/test/controllers/challenges.js +++ b/test/controllers/challenges.js @@ -592,7 +592,6 @@ describe('Challenges Controller', function() { res.body.success.should.be.true; res.body.status.should.equal(200); res.body.content.id.should.equal(fileId); - res.body.content.challengeId.should.equal(challenge.id); res.body.content.title.should.equal(reqData.title); res.body.content.fileName.should.equal(reqData.fileName); done(); diff --git a/test/controllers/files.js b/test/controllers/files.js index fbf3fef..ae636d4 100644 --- a/test/controllers/files.js +++ b/test/controllers/files.js @@ -127,7 +127,6 @@ describe('Files Controller', function() { res.body.success.should.be.true; res.body.status.should.equal(200); res.body.content.id.should.equal(fileId); - res.body.content.challengeId.should.equal(challenge.id); res.body.content.title.should.equal(reqData.title); res.body.content.fileName.should.equal(reqData.fileName); done(); diff --git a/test/swaggerTestHelper.js b/test/swaggerTestHelper.js index bfb4c7f..bc5b5bd 100644 --- a/test/swaggerTestHelper.js +++ b/test/swaggerTestHelper.js @@ -93,7 +93,7 @@ var _validate = function (route, obj, expectedResponseDefinition, definitions, c // if null if (!value) { // assert field not required - assert.ok(expectedResponseDefinition.required.indexOf(key) === -1, route + ': found null on required field ' + fieldPath); + assert.ok(expectedResponseDefinition.required && expectedResponseDefinition.required.indexOf(key) === -1, route + ': found null on required field ' + fieldPath); continue; } From cb6573a62eae25a7901bdae46364a544e474a6d4 Mon Sep 17 00:00:00 2001 From: Gian Franco Zabarino Date: Tue, 11 Nov 2014 14:18:04 -0300 Subject: [PATCH 2/2] - Added skipping test for filtering files (the skip method call can be removed when options {filtering: true} is set on the controller creation). - Fixed issue about including models into a query. --- lib/controllerHelper.js | 7 +++++-- test/controllers/challenges.js | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/controllerHelper.js b/lib/controllerHelper.js index df858c0..cc27d2e 100644 --- a/lib/controllerHelper.js +++ b/lib/controllerHelper.js @@ -203,11 +203,14 @@ function getAllEntities(model, referenceModels, lookupModel, options, req, res, } if (lookupModel) { var referenceIdField = routeHelper.getRefIdField(referenceModels[0]); + var include = { model: lookupModel, required: true }; if (referenceIdField in filters.where) { - filters.where[lookupModel.tableName + '.' + referenceIdField] = filters.where[referenceIdField]; + var where = {}; + where[referenceIdField] = filters.where[referenceIdField]; delete filters.where[referenceIdField]; + include.where = where; } - filters.include = [ lookupModel ]; + filters.include = [ include ]; } model.findAndCountAll(filters).success(function(result) { if (lookupModel) { diff --git a/test/controllers/challenges.js b/test/controllers/challenges.js index e5160b3..6595164 100644 --- a/test/controllers/challenges.js +++ b/test/controllers/challenges.js @@ -582,6 +582,24 @@ describe('Challenges Controller', function() { }); }); + it.skip('should able to get the all files with filters', function(done) { + // send request + request(url) + .get('/challenges/'+challenge.id+'/files?filter=size>3') + .end(function(err, res) { + // verify response + should.not.exist(err); + res.status.should.equal(200); + res.body.success.should.be.true; + res.body.status.should.equal(200); + res.body.should.have.property('metadata'); + res.body.metadata.totalCount.should.be.above(0); + res.body.should.have.property('content'); + res.body.content.length.should.be.above(0); + done(); + }); + }); + it('should able to get the existing file', function(done) { // send request request(url)