handler: function(request, reply) { if(global.NODE_ENV==="dev"){ Utils.logCallsite(Hoek.callStack()[1]); } // in this endpoint the only search condition is built-in in the param of the url // the Validate.ids function has transformed request.params.ids in an // array of objects ready to used in the postgres functions; // TODO: this array should be stored somewhere else console.log("request.params.ids: ", request.params.ids); var Seneca = request.server.plugins["seneca-promise"]["seneca"]; Seneca.actAsync({ role: "initiatives", cmd: "read", searchConditions: request.params.ids }) .then(function(data){ if (data.length === 0) { return reply(Boom.notFound("The resource does not exist.")); } return reply(data).code(200); }) .catch(function(err){ err = err.isBoom ? err : Boom.badImplementation(err.message); return reply(err); }); },
handler: function(request, reply) { if(global.NODE_ENV==="dev"){ Utils.logCallsite(Hoek.callStack()[1]); } // in this endpoint the only search condition is built-in in the param of the url var Seneca = request.server.plugins["seneca-promise"]["seneca"]; Seneca.actAsync({ role: "initiatives", cmd: "delete", searchConditions: request.params.ids[0] }) .then(function(deletedData){ if (deletedData.length === 0) { return reply(Boom.notFound("The resource does not exist.")); } return reply(deletedData).code(200); }) .catch(function(err){ err = err.isBoom ? err : Boom.badImplementation(err.message); return reply(err); }); },
method: function(request, reply){ Utils.logCallsite(Hoek.callStack()[0]); request.server.seneca.act("role: texts, cmd:readAll", function(err, data){ return reply(err || data); }); },
internals.shapesUpdate = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); ChangeCase(args.dbQuery, "underscored"); var ids = args.dbQuery.map(function(obj){ return { id: obj.id }; }); // 1) read the resources to be updated (to verify that they exist) Db.func('shapes_read', JSON.stringify(ids)) // 2) update the resources with the payload data .then(function(data) { if (data.length === 0) { throw Boom.notFound("The resource does not exist."); } // TODO: verify that data.length === args.ids.length return Db.func("shapes_update", JSON.stringify(args.dbQuery)) }) // 3) read again the updated resources (to obtain the joined data) .then(function(updatedData) { if (updatedData.length === 0) { throw Boom.badRequest("The resource could not be updated."); } var updatedIds = updatedData.map(function(obj){ return { id: obj.id }; }); return Db.func("shapes_read", JSON.stringify(updatedIds)); }) // 4) apply the object transform and reply .then(function(data){ if (data.length === 0) { throw Boom.notFound("The resource does not exist."); } data = args.raw === true ? data : Hoek.transform(data, internals.transformMap); return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }); };
handler: function(request, reply) { Utils.logCallsite(Hoek.callStack()[0]); var context = { }; return reply.view('404', { ctx: context }).code(404); },
method: function(request, reply){ Utils.logCallsite(Hoek.callStack()[0]); request.server.seneca.act({ role: "maps", cmd:"readMenu", pre: request.pre, }, function(err, data){ return reply(err || data); }); },
handler: function(request, reply) { if(global.NODE_ENV==="dev"){ Utils.logCallsite(Hoek.callStack()[1]); } var searchConditions = {}; // in this endpoint we can have query string search conditions; if there are // no query strings, it means we don't have any conditions (that is, read all!); // however note that some query strings might have default values, so there's always at // least one value if(request.query.moderationStatusId){ searchConditions["moderation_status_id"] = request.query.moderationStatusId; } if(request.query.typeId){ searchConditions["type_id"] = request.query.typeId; } var Seneca = request.server.plugins["seneca-promise"]["seneca"]; Seneca.actAsync({ role: "initiatives", cmd: "read", searchConditions: searchConditions }) .then(function(data){ if(request.query.csv){ // return promise return CsvStringify(data, {header: true}) .then(function(csv){ return reply(csv) .code(200) .header('content-type', 'text/csv') .header('content-disposition', 'attachment; filename="rede_convergir_initiatives.csv"') }); } return reply(data).code(200); }) .catch(function(err){ // the action should always return a boom error err = err.isBoom ? err : Boom.badImplementation(err.message); return reply(err); }); },
internals.geoTablesReadAll = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); Db.query('select * from geo_tables') .then(function(data) { data = args.raw === true ? data : Hoek.transform(data, internals.transformMapGeoTables); return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }); };
internals.textsReadAll = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); Db.func('texts_read') .then(function(data) { data = args.raw === true ? data : Hoek.transform(data, internals.transformMap); return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }); };
method: function(request, reply){ Utils.logCallsite(Hoek.callStack()[0]); Db.func("config_read", JSON.stringify({ key: "showEnglish" })) .then(function(configRow) { var showEnglish = configRow[0]["value"]; return reply(showEnglish); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return reply(err); }); },
internals.initiativesRead = function(args, done){ // TODO: add cache with catbox-memory here if(global.NODE_ENV==="dev"){ Utils.logCallsite(Hoek.callStack()[1]); } Db.func("initiatives_read", JSON.stringify(args.searchConditions)) .then(function(data) { data = args.raw === true ? data : Hoek.transform(data, internals.fromDbToPublicAPI); return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(Utils.getErrMsg(err)); return done(err); }); };
handler: function(request, reply) { Utils.logCallsite(Hoek.callStack()[0]); var context = { texts: _.indexBy(request.pre.texts, "id"), textsArray: request.pre.texts, maps: request.pre.maps, mapsMenu: request.pre.mapsMenu, auth: request.auth, urlParam1: "cartografia", showEnglish: request.pre.showEnglish }; return reply.view('cartografia', { ctx: context }); },
internals.textsDelete = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); Db.func('texts_delete', JSON.stringify(args.query)) .then(function(deletedData) { if (deletedData.length === 0) { throw Boom.notFound("The resource does not exist."); } return done(null, deletedData); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }); };
internals.textsCreate = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); ChangeCase(args.query, "underscored"); internals.removeNewLines(args.query[0].contents); internals.decodeImg(args.query[0].contents); internals.encodePercentages(args.query[0].contents); // 1) create the resources with the payload data Db.func('texts_create', JSON.stringify(args.query)) // 2) read the created resources (to obtain the joined data) .then(function(createdData) { if (createdData.length === 0) { throw Boom.badRequest("The resource could not be created."); } var createdIds = createdData.map(function(obj){ return { id: obj.id }; }); return Db.func("texts_read", JSON.stringify(createdIds)); }) // 3) apply the object transform and reply .then(function(data){ if (data.length === 0) { throw Boom.notFound("The resource does not exist."); } data = args.raw === true ? data : Hoek.transform(data, internals.transformMap); return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }); };
handler: function(request, reply) { if(global.NODE_ENV==="dev"){ Utils.logCallsite(Hoek.callStack()[1]); } // make sure the url fields have "http://" request.payload[0].url = internals.correctUrl(request.payload[0].url); request.payload[0].videoUrl = internals.correctUrl(request.payload[0].videoUrl); var Seneca = request.server.plugins["seneca-promise"]["seneca"]; Seneca.actAsync({ role: "initiatives", cmd: "upsert", data: request.payload[0] }) .then(function(createdData){ //console.log("createdData", createdData); if(Config.get("email:send")===true && createdData.length>=1){ console.log("send email") var emailCtx = { lang: "pt", name: createdData[0].name, contactName: createdData[0].contactName, email: createdData[0].email, }; // TODO: the name of the template should be in the payload var templateName = request.payload[0].emailTemplate; Utils.sendEmail(templateName, emailCtx); } return reply(createdData).code(201); }) .catch(function(err){ err = err.isBoom ? err : Boom.badImplementation(err.message); return reply(err); }); },
internals.textsRead = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); Db.func('texts_read', JSON.stringify(args.query)) .then(function(data) { if (data.length === 0) { throw Boom.notFound("The resource does not exist."); } data = args.raw === true ? data : Hoek.transform(data, internals.transformMap); return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }); };
handler: function(request, reply) { Utils.logCallsite(Hoek.callStack()[0]); if(!request.auth.isAuthenticated){ Utils.serverLog(["auth"], "requested " + request.path + " but authentication failed - will now redirect to /{lang}/login"); return reply.redirect("/" + request.params.lang + "/login"); } var context = { texts: _.indexBy(request.pre.texts, "id"), textsArray: request.pre.texts, auth: request.auth, urlParam1: "dashboard", showEnglish: request.pre.showEnglish }; return reply.view('dashboard', { ctx: context }); },
method: function(request, reply){ Utils.logCallsite(Hoek.callStack()[0]); // this is the original array from the db var texts = request.pre.texts; // move the properties "contents.pt" and "contents.en" to the top-level of the object; // TODO: generalize for other languages for(var i=0, l=texts.length; i<l; i++){ texts[i].pt = texts[i].contents.pt; texts[i].en = texts[i].contents.en; // if the content is empty for one of the languages, use the corresponding default text // if(!texts[i].pt || !texts[i].en){ // defaultText = _.findWhere(request.server.app.defaultTexts, {id: i}); // if(!defaultText){ // defaultText = { // contents: { // pt: "missing defaultText: " + i, // en: "missing defaultText: " + i // } // }; // } // texts[i].pt = texts[i].pt || defaultText.contents.pt; // texts[i].en = texts[i].en || defaultText.contents.en; // } } return reply(texts); },
internals.initiativesDelete = function(args, done){ if(global.NODE_ENV==="dev"){ Utils.logCallsite(Hoek.callStack()[1]); } debugger; // 1) delete the resource with the id param Db.func("initiatives_delete", JSON.stringify(args.searchConditions)) // 2) apply the object transform and reply .then(function(deletedData){ debugger; // this should never happen, but we check anyway if (deletedData.length === 0) { throw Boom.notFound("The resource was created/updated but does not exist anymore."); } deletedData = (args.raw === true) ? deletedData : Hoek.transform(deletedData, internals.fromDbToPublicAPI); return done(null, deletedData); }) // 4. handle errors .catch(function(err) { // PL/pgSQL Error "no_data_found"; // this will happen when the we try to delete an initiative that has been deleted // meanwhile (or that never existed) if(err.code === "P0002"){ err = Boom.notFound("The resource does not exist."); } err = err.isBoom ? err : Boom.badImplementation(Utils.getErrMsg(err)); return done(err); }); };
handler: function(request, reply) { Utils.logCallsite(Hoek.callStack()[0]); var viewFile = Utils.getView(request); console.log("viewFile: ", viewFile); // if the lang does not exist, show the 404 template with the default language (english) if(!request.params.lang){ return reply.view('404', { }).code(404); } // update the global "lang" variable in Nunjucks request.server.plugins["clima-web"].env.addGlobal("lang", request.params.lang); // if(Config.get('hapi.auth')===false){ // request.auth.credentials = Hoek.clone(Config.get("hapi.dummyCredentials")); // request.auth.isAuthenticated = true; // } // TODO: we can filter request.pre.texts to send only the texts for this page var context = { urlParam1: request.params.level1, urlParam2: request.params.level2, urlParam3: request.params.level3, urlParam4: request.params.level4, urlParam5: request.params.level5, urlWithoutLang: Utils.getUrlWithoutLang(request.params), auth: JSON.parse(JSON.stringify(request.auth)), showEnglish: request.pre.showEnglish //textsArray: request.pre.texts, //defaultTexts: request.server.app.defaultTexts }; // add the data available in request.pre that has been treated and ready to be used // in the nunjucks template: texts, textArray, files, etc for(var key in request.pre){ if(request.pre.hasOwnProperty(key)){ context[key] = request.pre[key]; } } //return reply({"abc": 123}) // to get the texts in the views, instead of the array we want an object whose keys are the ids context.editableTexts = context.texts.filter(function(obj){ return viewFile.indexOf(obj.pageName) >= 0; }); context.editableTexts = _.indexBy(context.editableTexts, "editableId"); context.texts = _.indexBy(context.texts, "id"); context.showEnglish = request.pre.showEnglish; //console.log("context.editableTexts: ", context.editableTexts); // context.editableTexts = _.indexBy(context.editableTexts, "editableId"); // console.log("context.editableTexts: ", context.editableTexts); var response = reply.view(viewFile, { ctx: context }); if(viewFile === "404"){ response.code(404); } return response; },
internals.shapesCreate = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); var shapesSchema = "geo"; var zipId = args.payload[0]["zipId"]; var fromSrid = args.payload[0]["fromSrid"]; var description = args.payload[0]["description"]; console.log("args.payload[0]: ", args.payload[0]) var zipFile = _.findWhere(args.files, {id: zipId}); if(!zipFile){ return done(Boom.badRequest("The zip file with the shapes does not exist in the system (wrong id?)")); } var zipName = zipFile.name; var zipExtname = Path.extname(zipName); var zipBasename = Path.basename(zipName, zipExtname); var zipPhysicalPath = zipFile.physical_path; var rootDir = Config.get("instanceRootDir"); if(zipExtname !== ".zip"){ return done(Boom.badRequest("The file must be a zip")); } var zipFullPath = Path.join(rootDir, zipPhysicalPath, zipName); var tempDir = Path.join(rootDir, zipPhysicalPath, "_extract_" + zipBasename + "_" + Utils.getRandomString()); var shpFile = "", shpBasename = "", tableName = ""; // step 1: delete tempDir (where the contents of the zip will be placed); if tempDir does not exists, // it won't do anything (err will be null); then create the dir, which will be empty for sure /* Possible errors in this step: - the directory can't be created */ internals.prepareDir(tempDir) // step 2: extract the zip into tempDir and verify that all the necessary files are present /* Possible errors in this step: - the zip is invalid (can't be extracted) - the zip has more than 1 .shp file (or no .shp files) - the zip doesn't have the necessary files: .shp, .prj, .shx and .dbf */ .then(function(){ var deferred = Q.defer(); Fs.createReadStream(zipFullPath) .pipe(Unzip.Extract({ path: tempDir })) .on("close", function(){ // the zip has been successfully extracted; now get an array // with the names of files in tempDir that have the .shp extension var extractedFiles = Fs.readdirSync(tempDir); var shpFiles = extractedFiles.filter(function(filename){ return Path.extname(filename) === ".shp"; }); if(shpFiles.length!==1){ return deferred.reject(Boom.badRequest("The zip must contain one .shp file (and only one)")); } shpFile = shpFiles[0]; shpBasename = Path.basename(shpFile, Path.extname(shpFile)); // make sure the zip file also has the other mandatory files var requiredFiles = [shpBasename + ".prj", shpBasename + ".shx", shpBasename + ".dbf"]; if(_.intersection(extractedFiles, requiredFiles).length < requiredFiles.length){ return deferred.reject(Boom.badRequest("The zip must contain files with the following extensions: .shp, .prj, .shx, .dbf")); } return deferred.resolve(); }) .on("error", function(err){ return deferred.reject(Boom.badRequest("The zip is invalid")); }); return deferred.promise; }) // step 3: execute shp2pgsql /* Possible errors in this step: - in the callback for child process' exec, the err argument is not undefined - in the callback for child process' exec, the stdout is not as expected - TODO: we might have to use "-W LATIN1" (default: "UTF-8") */ .then(function(){ var deferred = Q.defer(); // define the table name (based on the shape file basename, after being slugified) tableName = _s(shpBasename).slugify().replaceAll("-", "_").value(); if( _.findWhere(args.shapes, {table_name: tableName}) || _.findWhere(args.geoTables, {table_name: tableName}) ){ tableName = tableName + "_" + Utils.getRandomString(); } // the basic command is: // shp2pgsql -D -I -s 4326 <path-to-shp-file> <name-of-schema>.<name-of-the-table> | psql --dbname=<name-of-the-database> // however we might have to do srid conversion (option "-s", done in this step) and/or set the encoding to latin1 (option "-W", done in the next step) var tplString; if(fromSrid===4326){ tplString = 'shp2pgsql -D -I -s 4326 "<%= shapePath %>" <%= schema %>.<%= tableName %>'; } else{ // psql --help: "reprojects from given SRID (cannot be used with -D)" tplString = 'shp2pgsql -I -s <%= fromSrid %>:4326 "<%= shapePath %>" <%= schema %>.<%= tableName %>'; } var command1 = _.template(tplString); var command2 = _.template('psql --dbname=<%= dbName %>'); var command = command1({fromSrid: fromSrid, shapePath: Path.join(tempDir, shpFile), schema: shapesSchema, tableName: tableName}) + " | " + command2({dbName: Config.get("db.postgres.database") }); Utils.serverLog(["shp2pgsql"], command); // maxBuffer specifies the largest amount of data allowed on stdout or stderr (we set to 5mb) Exec(command, {maxBuffer: 1024 * 5000}, function(err, stdout, stderr){ if(err){ return deferred.reject(Boom.badImplementation("ChildProcess.exec error: " + err.message)); } if(_s.include(stdout.toLowerCase(), "create index") && _s.include(stdout.toLowerCase(), "commit")){ return deferred.resolve(); } else{ // TODO: stderr might be big (if the shape if also big)? return deferred.reject(Boom.badImplementation("shp2pgsql error: " + stderr)); } }); return deferred.promise; }) // step 4: execute shp2pgsql again, now with a different enconding (case the previous execution has failed) .catch(function(err){ // check if we had the "Unable to convert field name to UTF-8" error var encodingErr = /utf/i.test(err.message); if(!encodingErr){ throw err; } var deferred = Q.defer(); // the command is now: shp2pgsql -W "latin1" -D -I -s 4326 <path-to-shp-file> <name-of-schema>.<name-of-the-table> | psql --dbname=<name-of-the-database> var tplString; if(fromSrid===4326){ tplString = 'shp2pgsql -W "latin1" -D -I -s 4326 "<%= shapePath %>" <%= schema %>.<%= tableName %>'; } else{ // "reprojects from given SRID (cannot be used with -D)" tplString = 'shp2pgsql -W "latin1" -I -s <%= fromSrid %>:4326 "<%= shapePath %>" <%= schema %>.<%= tableName %>'; } var command1 = _.template(tplString); var command2 = _.template('psql --dbname=<%= dbName %>'); var command = command1({fromSrid: fromSrid, shapePath: Path.join(tempDir, shpFile), schema: shapesSchema, tableName: tableName}) + " | " + command2({dbName: Config.get("db.postgres.database") }); Utils.serverLog(["shp2pgsql"], command); // maxBuffer specifies the largest amount of data allowed on stdout or stderr (we set to 1mb) Exec(command, {maxBuffer: 1024 * 1000}, function(err, stdout, stderr){ if(err){ return deferred.reject(Boom.badImplementation("ChildProcess.exec error: " + err.message)); } if(_s.include(stdout.toLowerCase(), "create index") && _s.include(stdout.toLowerCase(), "commit")){ return deferred.resolve(); } else{ // TODO: stderr might be big (if the shape if also big)? return deferred.reject(Boom.badImplementation("shp2pgsql error: " + stderr)); } }); return deferred.promise; }) // step 5-7: the usual .then(function(){ var dbData = { schemaName: shapesSchema, tableName: tableName, srid: fromSrid, description: description, fileId: zipId, ownerId: args.payload[0]["ownerId"] }; ChangeCase(dbData, "underscored"); var promise = Db.func("shapes_create", JSON.stringify(dbData)); return promise; }) .then(function(createdData) { if (createdData.length === 0) { throw Boom.badRequest("The resource could not be created."); } var createdIds = createdData.map(function(obj){ return { id: obj.id }; }); var promise = Db.func("shapes_read", JSON.stringify(createdIds)); return promise; }) .then(function(data){ if (data.length === 0) { throw Boom.notFound("The resource does not exist."); } data = args.raw === true ? data : Hoek.transform(data, internals.transformMap); return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }) // whatever happens, we always want to delete the temporary directory where the zip was extracted .finally(function(){ console.log("delete temp dir"); Rimraf(tempDir, function(err){ if(err){ // nothing to do! } }); }); };
internals.shapesDelete = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); Db.func('shapes_delete', JSON.stringify(args.dbQuery)) .then(function(deletedData) { if (deletedData.length === 0) { throw Boom.notFound("The resource does not exist."); } return deletedData; }) // delete the zip file associated with this shape .then(function(data){ var deletedShape = _.findWhere(args.shapes, {id: data[0].deleted_id}); if(deletedShape.length == 0){ return deletedShape; } var deferred = Q.defer(); // http -v DELETE localhost:3000/api/v1/files/123 var uri = "http://localhost:" + Config.get("port") + "/api/v1/files/" + deletedShape.file_data.id; var options = { headers: { "content-type": "application/json" }, json: true }; if(args.headers.cookie){ options.headers.cookie = args.headers.cookie; } Wreck.delete(uri, options, function(err, response, payload){ //console.log("payload from DELETE /api/v1/files/xxx:: ", payload); if(response.statusCode === 200){ data[0].deleted_file_id = payload[0].deleted_id; } else if(response.statusCode >= 400 && response.statusCode < 500){ data[0].deleted_file_id = "None (" + payload.message + ")"; } // even if there was an error deleting the file, we always want the deferred to be resolved return deferred.resolve(data); }) return deferred.promise; }) .then(function(data){ return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }); };
internals.textsUpdate = function(args, done){ Utils.logCallsite(Hoek.callStack()[0]); debugger; // TODO: this next snippet is done in the validation; we are temporarily removing // the validation for PUT /api/texts because there is some strange error with joi if (_.isObject(args.query) && !_.isArray(args.query)) { args.query = [args.query]; } ChangeCase(args.query, "underscored"); var ids = args.query.map(function(obj){ return { id: obj.id }; }); internals.removeNewLines(args.query[0].contents); internals.decodeImg(args.query[0].contents); internals.encodePercentages(args.query[0].contents); // 1) read the resources to be updated (to verify that they exist) Db.func('texts_read', JSON.stringify(ids)) // 2) update the resources with the payload data .then(function(data) { if (data.length === 0) { throw Boom.notFound("The resource does not exist."); } // TODO: verify that data.length === args.ids.length return Db.func("texts_update", JSON.stringify(args.query)) }) // 3) read again the updated resources (to obtain the joined data) .then(function(updatedData) { if (updatedData.length === 0) { throw Boom.badRequest("The resource could not be updated."); } var updatedIds = updatedData.map(function(obj){ return { id: obj.id }; }); return Db.func("texts_read", JSON.stringify(updatedIds)); }) // 4) apply the object transform and reply .then(function(data){ if (data.length === 0) { throw Boom.notFound("The resource does not exist."); } data = args.raw === true ? data : Hoek.transform(data, internals.transformMap); return done(null, data); }) .catch(function(err) { err = err.isBoom ? err : Boom.badImplementation(err.msg, err); return done(err); }); };
internals.initiativesUpsert = function(args, done){ if(global.NODE_ENV==="dev"){ Utils.logCallsite(Hoek.callStack()[1]); } var data = Utils.changeCase(args.data, "underscored"); if(!data.slug){ data.slug = _s.slugify(data.name); } // 1) create/update the resources with the payload data (which is in data) Db.func("initiatives_upsert", JSON.stringify(data)) // 2) read the created/updated resources (to obtain the joined data) .then(function(upsertedData) { // this should never happen, but we check anyway if (upsertedData.length === 0) { throw Boom.badImplementation("The resource could not be created/updated."); } return Db.func("initiatives_read", JSON.stringify({id: upsertedData[0].id})); }) // 3) apply the object transform and reply .then(function(upsertedData){ // this is very unlikely to happen, but we check anyway if (upsertedData.length === 0) { throw Boom.notFound("The resource was created/updated but does not exist anymore."); } upsertedData = (args.raw === true) ? upsertedData : Hoek.transform(upsertedData, internals.fromDbToPublicAPI); return done(null, upsertedData); }) // 4. handle errors .catch(function(err) { // PL/pgSQL Error "no_data_found"; // this will happen when the we try to update an initiative that has been deleted meanwhile // (or that was never created) if(err.code === "P0002"){ err = Boom.notFound("The resource does not exist."); } // PL/pgSQL Error "unique_violation"; // this will happen when we try to update (insert?) a row with a repeated slug (already // present in some other project) if(err.code === "23505"){ if(err.detail.indexOf("slug") >= 0){ err = Boom.conflict("The provided value for slug is already in use by other initiative. Please choose a different slug."); } else{ err = Boom.conflict(); } } err = err.isBoom ? err : Boom.badImplementation(Utils.getErrMsg(err)); return done(err); }); };
handler: function(request, reply) { console.log("update: \n", request.payload[0]); if(global.NODE_ENV==="dev"){ Utils.logCallsite(Hoek.callStack()[1]); } // make sure the url fields have "http://" request.payload[0].url = internals.correctUrl(request.payload[0].url); request.payload[0].videoUrl = internals.correctUrl(request.payload[0].videoUrl); var Seneca = request.server.plugins["seneca-promise"]["seneca"]; var previousData = []; Seneca.actAsync({ role: "initiatives", cmd: "read", searchConditions: [{ id: request.payload[0].id }] // xxx }) .delay(500) .then(function(data){ if (data.length === 0) { return reply(Boom.notFound("The resource does not exist.")); } // bad hack - use function scope to place the retrieved data in a variable that can // be accessed in the next step previousData = data; //console.log("previousData: \n", previousData) return Seneca.actAsync({ role: "initiatives", cmd: "upsert", data: request.payload[0] }); }) .then(function(updatedData){ if(Config.get("email:send")===true && updatedData.length>=1 && previousData[0].moderationStatusId === "moderation_status_001_pending" && updatedData[0].moderationStatusId === "moderation_status_002_approved" ){ var emailCtx = { lang: "pt", name: updatedData[0].name, contactName: updatedData[0].contactName, email: updatedData[0].email, slug: updatedData[0].slug, }; // TODO: the name of the template should be in the payload var templateName = "approved"; Utils.sendEmail(templateName, emailCtx); } return reply(updatedData).code(200); }) .catch(function(err){ err = err.isBoom ? err : Boom.badImplementation(err.message); return reply(err); }); },