How can I DRY up my service in Angular 2 - service

I'm working on a Angular 2 service and I'm trying to DRY up my code. My service returns data from an RESTful API (GET request). For the moment i only send one param (auth_token) but how can i deal with a call to my service with extra params ?
Here my basic call to my service:
this._dashboardService.getData('users')
.subscribe(usersData=> {
if (usersData.ok) {
this.users= usersData.json();
});
Here is my service :
getData(data) {
let authToken = localStorage.getItem('auth_token');
let params = new URLSearchParams();
params.set('auth_token', authToken);
return this._http
.get(this._url+data,{ search: params })
.map(data=> {
if (data.status===200) {
return data;
}
});
}
I want to make this code work :
this._dashboardService.getData('users',oneParameter)
.subscribe(usersData=> {
if (usersData.ok) {
this.users= usersData.json();
});
I'm not sure how to proceed to make this (is it even possible ?)
Any ideas ?

You can pass your new object param and assign to another object that contains the auth_token.
getData(data, anotherParams) {
let params = new URLSearchParams();
let authToken = localStorage.getItem('auth_token');
var myObject = Object.assign({'auth_token':authToken}, anotherParams);
for (var name in myObject) {
params.set(name, myObject[name]);
}
return this._http
.get(this._url+data,{ search: params })
.map(data=> {
if (data.status===200) {
return data;
}
});
}

Related

AWS Lambda - MongoDB resource optimization

I'm building facebook chatbot using AWS Lambda and MongoDB. At the moment, my application is pretty simple but I'm trying to nail down the basics before I move onto the complex stuff.
I understand AWS Lambda is stateless but I've read adding below line in handler along with variables initialized outside handler, I don't have to establish DB connection on every request.
context.callbackWaitsForEmptyEventLoop = false;
(I've read this from this article; https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs)
I'm adding my entire code below
'use strict'
const
axios = require('axios'),
mongo = require('mongodb'),
MongoClient = mongo.MongoClient,
assert = require('assert');
var VERIFY_TOKEN = process.env.VERIFY_TOKEN;
var PAGE_ACCESS_TOKEN = process.env.PAGE_ACCESS_TOKEN;
var MONGO_DB_URI = process.env.MONGO_DB_URI;
let cachedDb = null;
let test = null;
exports.handler = (event, context, callback) => {
var method = event.context["http-method"];
context.callbackWaitsForEmptyEventLoop = false;
console.log("test :: " + test);
if (!test) {
test = "1";
}
// process GET request --> verify facebook webhook
if (method === "GET") {
var queryParams = event.params.querystring;
var rVerifyToken = queryParams['hub.verify_token']
if (rVerifyToken === VERIFY_TOKEN) {
var challenge = queryParams['hub.challenge'];
callback(null, parseInt(challenge))
} else {
var response = {
'body': 'Error, wrong validation token',
'statusCode': 403
};
callback(null, response);
}
// process POST request --> handle message
} else if (method === "POST") {
let body = event['body-json'];
body.entry.map((entry) => {
entry.messaging.map((event) => {
if (event.message) {
if (!event.message.is_echo && event.message.text) {
console.log("BODY\n" + JSON.stringify(body));
console.log("<<MESSAGE EVENT>>");
// retrieve message
let response = {
"text": "This is from webhook response for \'" + event.message.text + "\'"
}
// facebook call
callSendAPI(event.sender.id, response);
// store in DB
console.time("dbsave");
storeInMongoDB(event, callback);
}
} else if (event.postback) {
console.log("<<POSTBACK EVENT>>");
} else {
console.log("UNHANDLED EVENT; " + JSON.stringify(event));
}
})
})
}
}
function callSendAPI(senderPsid, response) {
console.log("call to FB");
let payload = {
recipient: {
id: senderPsid
},
message: response
};
let url = `https://graph.facebook.com/v2.6/me/messages?access_token=${PAGE_ACCESS_TOKEN}`;
axios.post(url, payload)
.then((response) => {
console.log("response ::: " + response);
}).catch(function(error) {
console.log(error);
});
}
function storeInMongoDB(messageEnvelope, callback) {
console.log("cachedDB :: " + cachedDb);
if (cachedDb && cachedDb.serverConfig.isConnected()) {
sendToAtlas(cachedDb.db("test"), messageEnvelope, callback);
} else {
console.log(`=> connecting to database ${MONGO_DB_URI}`);
MongoClient.connect(MONGO_DB_URI, function(err, db) {
assert.equal(null, err);
cachedDb = db;
sendToAtlas(db.db("test"), messageEnvelope, callback);
});
}
}
function sendToAtlas(db, message, callback) {
console.log("send to Mongo");
db.collection("chat_records").insertOne({
facebook: {
messageEnvelope: message
}
}, function(err, result) {
if (err != null) {
console.error("an error occurred in sendToAtlas", err);
callback(null, JSON.stringify(err));
} else {
console.timeEnd("dbsave");
var message = `Inserted a message into Atlas with id: ${result.insertedId}`;
console.log(message);
callback(null, message);
}
});
}
I did everything as instructed and referenced a few more similar cases but somehow on every request, "cachedDb" value is not saved from previous request and the app is establishing the connection all over again.
Then I also read that there is no guarantee the Lambda function is using the same container on multiple requests so I made another global variable "test". "test" variable value is logged "1" from the second request which means it's using the same container but again, "cachedDb" value is not saved.
What am I missing here?
Thanks in advance!
In short AWS Lambda function is not a permanently running service of any kind.
So, far I know AWS Lambda works on idea - "one container processes one request at a time".
It means when request comes and there is available running container for the Lambda function AWS uses it, else it starts new container.
If second request comes when first container executes Lambda function for first request AWS starts new container.
and so on...
Then there is no guarantee in what container (already running or new one) Lambda function will be executed, so... new container opens new DB connection.
Of course, there is an inactivity period and no running containers will be there after that. All will start over again by next request.

Debugging Ember-cli-mirage when routes are not being called

I have successfully created one route in ember-cli-mirage, but am having trouble loading the related data.
The API should be returning JSON API compliant data.
I'm not really sure if there are any good methods or not for debugging mirage's request interception. Here is my config.js
export default function() {
this.urlPrefix = 'https://myserver/';
this.namespace = 'api/v1';
this.get('/machines', function(db, request) {
return {
data: db.machines.map(attrs => (
{
type: 'machines',
id: attrs.id,
attributes: attrs
}
))
};
});
this.get('/machines/:id', function(db, request){
let id = request.params.id;
debugger;
return {
data: {
type: 'machines',
id: id,
attributes: db.machines.find(id),
relationships:{
"service-orders": db["service-orders"].where({machineId: id})
}
}
};
});
this.get('/machines/:machine_id/service-orders', function(db, request){
debugger; // this never gets caught
});
}
Most of this is working fine (I think). I can create machines and service orders in the factory and see the db object being updated. However, where my application would normally make a call to the api for service-orders: //myserver/machines/:machine_id/service-orders, the request is not caught and nothing goes out to the API
EDIT:
This is the route that my Ember app is using for /machines/:machine_id/service-orders:
export default Ember.Route.extend(MachineFunctionalRouteMixin, {
model: function() {
var machine = this.modelFor('machines.show');
var serviceOrders = machine.get('serviceOrders');
return serviceOrders;
},
setupController: function(controller, model) {
this._super(controller, model);
}
});
And the model for machines/show:
export default Ember.Route.extend({
model: function(params) {
var machine = this.store.find('machine', params.machine_id);
return machine;
},
setupController: function(controller, model) {
this._super(controller, model);
var machinesController = this.controllerFor('machines');
machinesController.set('attrs.currentMachine', model);
}
});
Intuitively, I would think that machine.get('serviceOrders'); would make a call to the API that would be intercepted and handled by Mirage. Which does not seem to be the case

sails.js the return object from service is undefined when using a find query

I created a service called AppService.
Its function getUserPostionOptions is supposed to return an object:
getUserPostionOptions: function (user) {
// PositionOptions.findOne({id:'53f218deed17760200778cfe'}).exec(function (err, positionOptions) {
var positionDirectionsOptions = [1,2,3];
var positionLengthsOptions = [4,5,6];
var object = {
directions:positionDirectionsOptions,
lengths:positionLengthsOptions
};
return object;
// });
}
This works, in my controller positionOptions gets populated correctly:
var positionOptions = AppService.getUserPostionOptions(user);
However, when I uncomment the find query the item is found but the object returns undefined.
Thank in advance for your help
SailsJs ORM (and almost NodeJs database querying methods) uses non-blocking mechanism via callback function. So you have to change your code into:
getUserPostionOptions: function (user, callback) {
PositionOptions.findOne({id:'53f218deed17760200778cfe'}).exec(function (err, positionOptions) {
var positionDirectionsOptions = [1,2,3];
var positionLengthsOptions = [4,5,6];
var object = {
directions:positionDirectionsOptions,
lengths:positionLengthsOptions
};
callback(null, object); // null indicates that your method has no error
});
}
Then just use it:
AppService.getUserPostionOptions(user, function(err, options) {
if (!err) {
sails.log.info("Here is your received data:");
sails.log.info(options);
}
});

How to use URLs like '/update/:id' as KendoUI datasource?

I read the documentation but found nothing related to setting parameters in dataSource urls. Is it possible to achieve that?
Thx in advance.
Yes, it is possible. The urls defined in the DataSource.transport might be a function. This function receives (for update) as first argument the data being updated (the model) and returns the string that should be used as URL.
Composing the URL for what you want to do is:
var ds = new kendo.data.DataSource({
transport: {
read: {
url: function () {
return 'read';
}
},
update: {
url : function (item) {
return 'update/' + item.id;
}
}
}
});
The answer seems to be vague on 'item.'
Just note that 'item' is an object. In fact anything passed in to read has to be an object, that's what Kendo expects. If you pass anything else into read, like a string, it will convert it into an object which isn't what you want. So, the solution is as follows:
_viewModel: kendo.observable({
items: new kendo.data.DataSource({
transport: {
read: {
url: function (args) {
var urlParm = '?take=' + 1 + '&skip=0&page=1&pageSize=' + 1;
return CGI_ISD._base + 'api/executionsummary/executiondetails/' + args.msgId + urlParm;
},
dataType: "json"
},
},
schema: {
data: function (response) {
return response.AggregateData.Data;
}
}
}),
}),
_reload: function (msgId) {
this._viewModel.items.read({msgId: msgId});
}
Short answer:
Nope.
Long answer:
Parameters are passed either inline with the url parameter of the transport object...
var id = 'abc123';
var ds = new kendo.data.DataSource({
transport: {
read: {
url: 'api/employees?id=' + id
}
}
});
...or they are passed in the data parameter of the transport object.
var id = 'abc123';
var ds = new kendo.data.DataSource({
transport: {
read: {
url: 'api/employees',
data: {
id: id;
}
}
}
});
or
var id = 'abc123';
var ds = new kendo.data.DataSource({
transport: {
read: {
url: 'api/employees',
data: function () {
return { id : id };
}
}
}
});

How to return an array of ParseObject from CloudCode call

There is a parse CloudCode function created as such:
Parse.Cloud.define("getCurrentEvents", function(request, response) {
var TimedEvent = Parse.Object.extend("TimedEvent");
var query = new Parse.Query(TimedEvent);
query.greaterThan("expiresOn", new Date());
query.find({
success: function(results) {
response.success(results);
},
error: function(error) {
response.error("There was an error while looking for TimedEvents");
}
});
});
It returns an array of TimedEvent, as shown in the curl test here:
{"result":[{"expiresOn":{"__type":"Date","iso":"2014-07-31T22:31:00.000Z"},"playMode":"Normal","tableId":"Carnival","objectId":"J1LSO3EnKi","createdAt":"2014-07-28T21:48:22.983Z","updatedAt":"2014-07-28T22:32:14.304Z","__type":"Object","className":"TimedEvent"}]}
When trying to access it from Unity SDK however, I get a "cannot convert to destination type" exception with the following line:
System.Threading.Tasks.Task<Parse.ParseObject[]> task =
Parse.ParseCloud.CallFunctionAsync<Parse.ParseObject[]> ("getCurrentEvents", parameters);
I also tried
System.Threading.Tasks.Task<IEnumerable<Parse.ParseObject>> task =
Parse.ParseCloud.CallFunctionAsync<IEnumerable<Parse.ParseObject[]>> ("getCurrentEvents", parameters);
with the same (lack of) results. What kind of signature is the SDK expecting?
Have you tried something like this (without IEnumerable?):
Threading.Tasks.Task<Parse.ParseObject> task = Parse.ParseCloud.CallFunctionAsync<Parse.ParseObject>("getCurrentEvents", parameters);
But better yet, you could extend ParseObject to create your own TimedEvent class in Unity, like this:
[ParseClassName("TimeEvent")]
public class TimeEvent : ParseObject
{
[ParseFieldName("expiresOn")]
public DateTime expiresOn
{
get { return GetProperty<DateTime>("expiresOn"); }
set { SetProperty(value, "expiresOn"); }
}
[ParseFieldName("playMode")]
public string playMode
{
get { return GetProperty<string>("playMode"); }
set { SetProperty(value, "playMode"); }
}
[ParseFieldName("tableId")]
public string tableId
{
get { return GetProperty<string>("tableId"); }
set { SetProperty(value, "tableId"); }
}
// any other fields you want to access
}
Then you can query your data like this:
IEnumerator getTimedEvents(Dictionary<string, object> parameters)
{
var cloudTask = Parse.ParseCloud.CallFunctionAsync<TimeEvent>("getCurrentEvents", parameters);
while (!cloudTask.IsCompleted)
yield return null;
if (cloudTask.IsCanceled || cloudTask.IsFaulted)
{
// handle error
}
else
{
TimeEvent t = cloudTask.Result;
// do stuff with t
}
}
P.S. Don't forget to register your Parse class somewhere (I usually do it in the Awake() of an early GameObject). In your case, you would do it like this:
Parse.ParseObject.RegisterSubclass<TimedEvent>();