Commit e323f167 authored by schi's avatar schi
Browse files

Commit for WGN project

parents
# FirstLife Backend
## Introduction
This document describes how to setup and configure the backend of the FirstLife component within the WeGovNow platform.
This component is responsible for the creation and managing of FirstLife entities.
## Prerequisites
WegGovNow FirstLife Backend component requires the availability of
* Strongloop framework, version 3.x ([https://strongloop.com/](https://strongloop.com/))
* mongo DB database, version >3.x ([https://www.mongodb.com/](https://www.mongodb.com/))
* NodeJS, version >8.x ([https://nodejs.org](https://nodejs.org))
* ImageMagick library ([https://www.imagemagick.org/](https://www.imagemagick.org/))
## Installation
To install the FirstLife backend, follow the reported steps:
1. Install Strongloop
* ```npm install -g strongloop```
2. Clone gitlab repository
* ```git clone https://gitlab.di.unito.it/wgn/firstlife-backend.git```
3. Download required Node packages
* ```cd firstlife-backend```
* ```npm install```
4. Create required subfolders
* ```mkdir uploads```
* ```mkdir server/logs```
## Configuration
Before launching the backend server, please follow the instructions:
1. Update the UWUM certificate configuration file ```fl_commons/uwum_prototypes.json```
2. Store the certificates in the folders reported in ```fl_commons/uwum_prototypes.json```
3. Setup the MongoDB connection parameters in ```fl_commons/db_handler.js``` and ```server/datasources.json```
4. Setup the API parameters in ```server/config.json```
5. Setup the FirstLife environment configuration in ```server/boot/constant.js```
## Running the server
Run the backend by using the command (remeber to configure ```server/boot/constant.js```s):
1. Production
* ```node server/server.js --env=PRODUCTION```
2. Test in localhost
* ```node server/server.js --env=DEVELOPMENT```
## Client
This is the place for your application front-end files.
var app = require('../../server/server');
var loopback = require('loopback');
var conf = require('../../server/boot/constant');
module.exports = function (IMCsubmitted) {
IMCsubmitted.observe("before save", function (ctx, next) {
console.log("IMCsubmitted BEFORE SAVE");
var errors = [];
if (ctx.instance) {
//NUOVA ISTANZA
ctx.instance.timestamp_insert = new Date();
ctx.instance.last_update = new Date();
ctx.instance.status = 1;
next();
} else {
//UPDATE
ctx.data.last_update=new Date();
next();
}
});
IMCsubmitted.observe("after save", function (ctx, next) {
console.log("IMCsubmitted AFTER SAVE");
if (ctx.isNewInstance) {
next();
} else {
next();
}
});
};
{
"name": "IMCsubmitted",
"plural": "IMCsubmitted",
"replaceOnPUT": true,
"base": "PersistedModel",
"strict": false,
"idInjection": false,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "string",
"required": true,
"index": true,
"id": true
},
"issue_id": {
"type": "number",
"required": true,
"index": true
},
"external_url": {
"type": "string",
"required": true,
"index": true
},
"issue_of": {
"type": "string",
"required": false
},
"actor": {
"type": "string",
"required": true
},
"name": {
"type": "string",
"required": true
},
"description": {
"type": "string",
"required": false,
"index": true
},
"category": {
"type": "string",
"required": true
},
"state": {
"type": "string",
"required": true
},
"latitude": {
"type": "number",
"required": true
},
"longitude": {
"type": "number",
"required": true
},
"address": {
"type": "string",
"required": false
},
"domain_id": {
"type": "number",
"required": false
},
"wgn_platform": {
"type": "string",
"required": true
},
"timestamp_insert": {
"type": "date",
"required": true,
"index": true
},
"last_update": {
"type": "date",
"required": true,
"index": true
},
"status": {
"type": "number",
"required": true,
"index": true
}
},
"validations": [],
"relations": {
},
"acls": [],
"methods": {}
}
var fl_utils = require("../../fl_commons/utils");
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
var myDb = null;
var url = 'mongodb://localhost:27017/fl2';
MongoClient.connect(url, {
poolSize: 30
}, function (err, db) {
assert.equal(null, err);
console.log("Connected correctly to MongoDB server");
myDb = db;
//db.close();
});
module.exports = function (Area) {
Area.observe("before save", function (ctx, next) {
console.log("Area BEFORE SAVE");
var errors = [];
if (ctx.instance) {
//NUOVA ISTANZA
console.log("Performing validation...");
/*errors = errors.concat(fl_utils.validateProperties(ctx.instance));
errors = errors.concat(fl_utils.validateCategories(ctx.instance));*/
errors = errors.concat(fl_utils.validateGeoJson(ctx.instance));
if (errors.length > 0) {
var error = new Error(errors);
next(error);
}
if (ctx.instance.properties) {
ctx.instance.properties.timestamp_insert = new Date();
ctx.instance.properties.last_update = new Date();
ctx.instance.properties.aggregation = {};
if (!ctx.instance.properties.tiles)
ctx.instance.properties.tiles = [];
if (ctx.instance.properties.parent_id)
ctx.instance.parent_id = ctx.instance.properties.parent_id;
}
next();
} else {
//UPDATE
console.log("UPDATE");
//errors = errors.concat(fl_utils.validateGeoJson(ctx.data));
if (errors.length > 0) {
var error = new Error(errors);
next(error);
}
/*if (ctx.data.properties && ctx.data.properties.area_id) {
ctx.data.area_id=ctx.data.properties.area_id;
}*/
//ctx.data.properties.last_update = new Date();
if (ctx.data.properties) {
if (ctx.data.properties.parent_id)
ctx.data.parent_id = ctx.data.properties.parent_id;
}
next();
}
});
Area.observe("after save", function (ctx, next) {
console.log("Area AFTER SAVE");
if (ctx.isNewInstance) {
// NUOVA ISTANZA
//valorizza properties.id al valore assegnato da mongo
ctx.instance.properties.id = ctx.instance.id;
//ctx.instance.updateAttribute("properties", ctx.instance.properties);
next();
} else {
console.log("UPDATE");
next();
}
});
Area.push_tile = function (area_id, tile_id, cb) {
console.log(tile_id + " - INIZIO");
Area.findById(area_id, function (err, instance) {
if (!err) {
if (instance) {
console.log(tile_id + " - ISTANZA TROVATA");
if (instance.properties.tiles.indexOf(tile_id) == -1) {
instance.properties.tiles.push(tile_id);
instance.updateAttribute("properties.tiles", instance.properties.tiles, function (err, newInstance) {
console.log(tile_id + " - PUSH");
cb(null, {
tiles: newInstance.properties.tiles
});
});
} else {
console.log(tile_id + " - NO PUSH");
cb(null, {
tiles: instance.properties.tiles
});
}
} else {
var e = new Error();
e.status = 404;
e.message = "Area " + area_id + " not found";
console.log(tile_id + " - ERRORE");
cb(e, null);
}
} else {
var e = new Error();
e.status = 500;
e.message = "Error in finding area " + area_id;
cb(e, null);
}
});
};
Area.push_tile_v2 = function (area_id, tile_id, cb) {
console.log(tile_id + " - INIZIO");
Area.findById(area_id, function (err, instance) {
if (!err) {
if (instance) {
console.log(tile_id + " - ISTANZA TROVATA");
Area.updateAll(
{id: area_id},
{'$addToSet': {'properties.tiles': tile_id}},
{allowExtendedOperators: true}, function(err, info) {
if (err) {
var e = new Error();
e.status = 500;
e.message = "Error in updating tiles";
console.log(tile_id + " - ERRORE");
cb(e, null);
} else {
cb(null, {message:"tiles updated"});
}
}
);
} else {
var e = new Error();
e.status = 404;
e.message = "Area " + area_id + " not found";
console.log(tile_id + " - ERRORE");
cb(e, null);
}
} else {
var e = new Error();
e.status = 500;
e.message = "Error in finding area " + area_id;
cb(e, null);
}
});
};
Area.remoteMethod(
'push_tile_v2',
{
accepts: [
{arg: 'area_id', type: 'Number', required: true},
{arg: 'tile_id', type: 'String', required: true}
],
returns: {arg: 'data', type: 'Array'},
http: {path: '/:area_id/add_tile', verb: 'put'}
}
);
Area.delete_all_areas = function (cb) {
Area.destroyAll({}, function (err, info) {
if (!err) {
cb(null, info);
} else {
var e = new Error();
e.status = 500;
e.message = "Error in deleting areas";
cb(e, null);
}
});
};
Area.remoteMethod(
'delete_all_areas',
{
accepts: [],
returns: {arg: 'data', type: 'Any'},
http: {path: '/delete_all', verb: 'delete'}
}
);
Area.afterRemote('create', function (context, respInstance, next) {
console.log('AREA create afterRemote');
var instance = respInstance;
areaIndexing(instance);
next();
});
function areaIndexing(area) {
console.log("INDEXING");
var collection = myDb.collection("Thing");
// query per cercare i marker contenuti nell'area
var query = {
"geometry": {
$geoWithin: {
$geometry: area.geometry
}
}
};
collection.find(
query
).toArray(function (err, entities) {
// entities contains the set of markers included in the area
//console.log(entities.length);
if (entities && entities.length > 0) {
// there is at least one marker to index into the area
var aggregation = {
entities: {}
};
entities.forEach(function (entity) {
var obj = {
id: entity.id,
domain_id: entity.properties.domain_id,
entity_type: entity.properties.entity_type,
name: entity.properties.name,
zoom_level: entity.properties.zoom_level
};
if (!aggregation.entities[obj.domain_id]) {
aggregation.entities[obj.domain_id] = [];
}
aggregation.entities[obj.domain_id].push(obj);
// todo settare area_id delle Thing collegate che stanno a zoom_level COMPATIBILE con l'area
/*entity.updateAttribute("area_id", area.id, function (err, instance) {
if (err) {
console.log("ERROR");
}
});*/
});
console.log("end");
console.log(aggregation);
// Update area aggregation object
Area.findById(area.id, function (err, instance) {
instance.updateAttribute("properties.aggregation", aggregation, function (err, i) {
console.log(i);
})
});
}
});
}
};
{
"name": "Area",
"replaceOnPUT": false,
"base": "PersistedModel",
"description": "FirstLife area",
"strict": false,
"idInjection": false,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "string",
"required": true,
"id":true,
"comments": "area index"
},
"geometry": {
"type": "any",
"required": true
},
"type": {
"type": "String",
"required": true,
"comments": "\"Feature\""
},
"parent_id": {
"type": "Number",
"required": false,
"comments": "Parent area id"
}
},
"validations": [],
"relations": {
"prop": {
"type": "embedsOne",
"model": "AreaProperties",
"property": "properties",
"options": {
"validate": true,
"forceId": false
}
},
"children": {
"type": "hasMany",
"model": "Area",
"foreignKey": "parent_id"
},
"parent": {
"type": "belongsTo",
"model": "Area",
"foreignKey": "parent_id"
},
"entities_deep": {
"type": "hasMany",
"model": "Thing",
"foreignKey": "entity_id",
"through": "EntityAreaRel"
},
"entities": {
"type": "hasMany",
"model": "Thing",
"foreignKey": "area_id"
}
},
"acls": [],
"methods": {},
"hidden":["parent_id"]
}
\ No newline at end of file
module.exports = function(AreaProperties) {
};
{
"name": "AreaProperties",
"replaceOnPUT": false,
"base": "PersistedModel",
"strict": false,
"idInjection": false,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "string",
"required": false
},
"parent_id": {
"type": "number",
"required": false
},
"source_id": {
"type": "string",
"required": true
},
"source": {
"type": "string",
"required": true
},
"zoom_min": {
"type": "number",
"required": true
},
"zoom_max": {
"type": "number",
"required": true
},
"name": {
"type": "string",
"required": false
},
"description": {
"type": "string",
"required": false
},
"type": {
"type": "string",
"required": false
},
"tiles": {
"type": "array",
"required": false