Issue with notification hub - node.js

I am new to azure notification hub. I tried from the documentation. But not able to do the name . Need some help on this .
Tried from the below link
How to register devices to Azure Notification Hub from server side(with NodeJS sdk) ?
Not sure about the params.
var azure = require('azure-sb');
var notificationHubService = azure.createNotificationHubService('<Hub Name>','<Connection String>');
var payload={
alert: 'Hello!'
};
notificationHubService.createRegistrationId(function(error, registrationId, response){
if(!error){
console.log(response);
console.log(registrationId);
//RegistrationDescription registration = null;
//registration.RegistrationId = registrationId;
//registration.DeviceToken = req.body.token;
notificationHubService.apns.createOrUpdateNativeRegistration(registrationId, req.body.token, req.token.upn, function(error, response){
if(!error){
console.log('Inside : createOrUpdateNativeRegistration' + response);
notificationHubService.apns.send(null, payload, function(error){
if(!error){
// notification sent
console.log('Success: Inside the notification send call to Hub.');
}
});
}
else{
console.log('Error in registering the device with Hub' + error);
}
});
}
else{
console.log('Error in generating the registration Id' + error);
}
});
While creating registrationID which registration id i have to pass there. What is request.body.token and what is request.token.upn. I need it for apns

While creating registrationId , you dont have to pass any id. **createRegistrationId(callback)** takes callback as a parameter which creates a registration identifier.
As per the overall implemetation:
/**
* Creates a registration identifier.
*
* #param {Function(error, response)} callback `error` will contain information
* if an error occurs; otherwise, `response`
* will contain information related to this operation.
*/
NotificationHubService.prototype.createRegistrationId = function (callback) {
validateCallback(callback);
var webResource = WebResource.post(this.hubName + '/registrationids');
webResource.headers = {
'content-length': null,
'content-type': null
};
this._executeRequest(webResource, null, null, null, function (err, rsp) {
var registrationId = null;
if (!err) {
var parsedLocationParts = url.parse(rsp.headers.location).pathname.split('/');
registrationId = parsedLocationParts[parsedLocationParts.length - 1];
}
callback(err, registrationId, rsp);
});
};
Once you are done with RegistrationID Creation then you can call createOrUpdateRegistration(registration, optionsopt, callback) and here is the overall implementation for the same:
/**
* Creates or updates a registration.
*
* #param {string} registration The registration to update.
* #param {object} [options] The request options or callback function. Additional properties will be passed as headers.
* #param {object} [options.etag] The etag.
* #param {Function(error, response)} callback `error` will contain information
* if an error occurs; otherwise, `response`
* will contain information related to this operation.
*/
NotificationHubService.prototype.createOrUpdateRegistration = function (registration, optionsOrCallback, callback) {
var options;
azureutil.normalizeArgs(optionsOrCallback, callback, function (o, c) { options = o; callback = c; });
validateCallback(callback);
if (!registration || !registration.RegistrationId) {
throw new Error('Invalid registration');
}
var webResource = WebResource.put(this.hubName + '/registrations/' + registration.RegistrationId);
registration = _.clone(registration);
var registrationType = registration[Constants.ATOM_METADATA_MARKER]['ContentRootElement'];
delete registration[Constants.ATOM_METADATA_MARKER];
delete registration.ExpirationTime;
delete registration.ETag;
if (!registration.Expiry) {
delete registration.Expiry;
}
registration.BodyTemplate = '<![CDATA[' + registration.BodyTemplate + ']]>';
var registrationXml = registrationResult.serialize(registrationType, registration);
this._executeRequest(webResource, registrationXml, registrationResult, null, callback);
};
You can find the complete implementation of NotificationHubService.js here.
Hope it helps.

Related

Creating and Capturing payment with paypal v2 node.js

I am trying to integrate PayPal server-side payment on a website with a new v2 since v1 is deprecated. Since I had to include quite a lot of code from their git, i will post here everything for a working example
In v1 i could do it really easily:
app.get('/create', function (req, res) {
//build PayPal payment request
let payReq = JSON.stringify({
'intent': 'sale',
'redirect_urls': {
'return_url': 'http://localhost:8081/process',
'cancel_url': 'http://localhost:8081/cancel'
},
'payer': {
'payment_method': 'paypal'
},
'transactions': [{
'amount': {
'total': '7.47',
'currency': 'USD'
},
'description': 'This is the payment transaction description.'
}]
});
paypal.payment.create(payReq, function (error, payment) {
if (error) {
console.error(error);
} else {
//capture HATEOAS links
let links = {};
payment.links.forEach(function (linkObj) {
links[linkObj.rel] = {
'href': linkObj.href,
'method': linkObj.method
};
})
//if redirect url present, redirect user
if (links.hasOwnProperty('approval_url')) {
res.redirect(links['approval_url'].href);
} else {
console.error('no redirect URI present');
}
}
});
});
app.get('/process', function (req, res) {
let paymentId = req.query.paymentId;
let payerId = {'payer_id': req.query.PayerID};
paypal.payment.execute(paymentId, payerId, function (error, payment) {
if (error) {
console.error(error);
} else {
if (payment.state == 'approved') {
const payerCountry = payment.payer.payer_info.country_code;
const total = payment.transactions[0].amount.total;
const currency = payment.transactions[0].amount.currency;
savePayment(payerCountry, total, currency);
res.send('payment completed successfully ' + cnt++);
} else {
res.send('payment not successful');
}
}
});
});
The create endpoint basically creates the order, the paypal API returns hateos links and the controller says the browser should redirect to that link. Once redirected , if user approves payment on paypal site he is redirected to on of
'redirect_urls': {
'return_url': 'http://localhost:8081/process',
'cancel_url': 'http://localhost:8081/cancel'
},
process endpoints retrieves PAYMENT ID and PAYER ID from query and captures the order - allowing me to do whatever i want to do ( e.g save payment in db ) in callback.
Now v2 seems like huge mess:
Following devs guide i have created
Paypal button (copy paste ):
import React, {useEffect} from "react";
import {PayPalButtons, PayPalScriptProvider, usePayPalScriptReducer} from "#paypal/react-paypal-js";
// This values are the props in the UI
const amount = "2";
const currency = "USD";
const style = {"layout": "vertical"};
const ButtonWrapper = ({currency, showSpinner}) => {
// usePayPalScriptReducer can be use only inside children of PayPalScriptProviders
// This is the main reason to wrap the PayPalButtons in a new component
const [{options, isPending}, dispatch] = usePayPalScriptReducer();
useEffect(() => {
dispatch({
type: "resetOptions",
value: {
...options,
currency: currency,
},
});
}, [currency, showSpinner]);
const createOrder = (data, actions) => {
console.log("create")
return fetch('http://localhost:8081/create', {
method: 'post'
}).then(function (res) {
return res.json();
}).then(function (orderData) {
console.log(orderData);
window.location = orderData.redirect;
});
}
// Call your server to finalize the transaction
const onApprove = (data, actions) => {
console.log("approve")
return fetch('/process', {
method: 'post'
}).then(function (res) {
return res.json();
}).then(function (orderData) {
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show confirmation or thank you
// This example reads a v2/checkout/orders capture response, propagated from the server
// You could use a different API or structure for your 'orderData'
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart(); // Recoverable state, per:
// https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
}
if (errorDetail) {
var msg = 'Sorry, your transaction could not be processed.';
if (errorDetail.description) msg += '\n\n' + errorDetail.description;
if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
return alert(msg); // Show a failure message (try to avoid alerts in production environments)
}
// Successful capture! For demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
alert('Transaction ' + transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
});
}
return (<>
{(showSpinner && isPending) && <div className="spinner"/>}
<PayPalButtons
style={style}
disabled={false}
forceReRender={[amount, currency, style]}
fundingSource={undefined}
createOrder={(data, actions) => createOrder(data, actions)}
onApprove={(data, actions) => onApprove(data, actions)}
/>
</>
);
}
export default function PayPalButton() {
return (
<div style={{ maxWidth: "750px", minHeight: "200px" }}>
<PayPalScriptProvider
options={{
"client-id": "test",
components: "buttons",
currency: "USD"
}}
>
<ButtonWrapper
currency={currency}
showSpinner={false}
/>
</PayPalScriptProvider>
</div>
);
}
And then followed the flow from paypal's github example
created their HttpClient
const checkoutNodeJssdk = require('#paypal/checkout-server-sdk');
/**
* Returns PayPal HTTP client instance with environment which has access
* credentials context. This can be used invoke PayPal API's provided the
* credentials have the access to do so.
*/
function client() {
return new checkoutNodeJssdk.core.PayPalHttpClient(environment());
}
/**
* Setting up and Returns PayPal SDK environment with PayPal Access credentials.
* For demo purpose, we are using SandboxEnvironment. In production this will be
* LiveEnvironment.
*/
function environment() {
let clientId = process.env.PAYPAL_CLIENT_ID || '<clientId>';
let clientSecret = process.env.PAYPAL_CLIENT_SECRET || '<secret>';
if (process.env.NODE_ENV === 'production') {
return new checkoutNodeJssdk.core.LiveEnvironment(clientId, clientSecret);
}
return new checkoutNodeJssdk.core.SandboxEnvironment(clientId, clientSecret);
}
async function prettyPrint(jsonData, pre=""){
let pretty = "";
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}
for (let key in jsonData){
if (jsonData.hasOwnProperty(key)){
if (isNaN(key))
pretty += pre + capitalize(key) + ": ";
else
pretty += pre + (parseInt(key) + 1) + ": ";
if (typeof jsonData[key] === "object"){
pretty += "\n";
pretty += await prettyPrint(jsonData[key], pre + "\t");
}
else {
pretty += jsonData[key] + "\n";
}
}
}
return pretty;
}
module.exports = {client: client, prettyPrint:prettyPrint};
created their CreateOrder:
/**
* PayPal SDK dependency
*/
const checkoutNodeJssdk = require('#paypal/checkout-server-sdk');
/**
* PayPal HTTP client dependency
*/
const payPalClient = require('./PayPalClient');
/**
* Setting up the JSON request body for creating the Order. The Intent in the
* request body should be set as "CAPTURE" for capture intent flow.
*
*/
function buildRequestBody() {
return {
"intent": "CAPTURE",
"application_context": {
"return_url": "http://localhost:8081/process",
"cancel_url": "https://www.example.com",
"locale": "en-US",
"landing_page": "BILLING",
"user_action": "CONTINUE"
},
"purchase_units": [
{
"soft_descriptor": "Donation",
"amount": {
"currency_code": "USD",
"value": "220.00"
}
}
]
};
}
/**
* This is the sample function which can be sued to create an order. It uses the
* JSON body returned by buildRequestBody() to create an new Order.
*/
async function createOrder(debug=false) {
try {
const request = new checkoutNodeJssdk.orders.OrdersCreateRequest();
request.headers["prefer"] = "return=representation";
request.requestBody(buildRequestBody());
const response = await payPalClient.client().execute(request);
if (debug){
console.log("Status Code: " + response.statusCode);
console.log("Status: " + response.result.status);
console.log("Order ID: " + response.result.id);
console.log("Intent: " + response.result.intent);
console.log("Links: ");
response.result.links.forEach((item, index) => {
let rel = item.rel;
let href = item.href;
let method = item.method;
let message = `\t${rel}: ${href}\tCall Type: ${method}`;
console.log(message);
});
console.log(`Gross Amount: ${response.result.purchase_units[0].amount.currency_code} ${response.result.purchase_units[0].amount.value}`);
// To toggle print the whole body comment/uncomment the below line
console.log(JSON.stringify(response.result, null, 4));
}
return response;
}
catch (e) {
console.log(e)
}
}
/**
* This is the driver function which invokes the createOrder function to create
* an sample order.
*/
if (require.main === module){
(async() => await createOrder(true))();
}
/**
* Exports the Create Order function. If needed this can be invoked from the
* order modules to execute the end to flow like create order, retrieve, capture
* and refund(Optional)
*/
module.exports = {createOrder:createOrder};
And endpoints:
const createUsersOrder = async (res) => {
try {
let response = await createOrder();
console.log("Creating Order...");
let orderId = "";
if (response.statusCode === 201){
console.log("Created Successfully");
orderId = response.result.id;
console.log("Links:");
response.result.links.forEach((item, index) => {
let rel = item.rel;
let href = item.href;
let method = item.method;
let message = `\t${rel}: ${href}\tCall Type: ${method}`;
console.log(message);
});
let links = {};
response.result.links.forEach(function (linkObj) {
links[linkObj.rel] = {
'href': linkObj.href,
'method': linkObj.method
};
})
//if redirect url present, redirect user
if (links.hasOwnProperty('approve')) {
var returnObj = {redirect : links['approve'].href}
console.log("Returning " + returnObj)
res.send(returnObj);
} else {
console.error('no redirect URI present');
}
}
console.log("Copy approve link and paste it in browser. Login with buyer account and follow the instructions.\nOnce approved hit enter...");
return
} catch (error) {
console.log('There was an error: ', error);
}
};
app.post("/create", function(req,res) {
createUsersOrder(res);
})
Here, this is called when button is clicked, as "createOrder" method is called. I create order just like in the v1 code, and then redirect browser to the url. However when the user approves transaction on paypal, and thus is being redirected to one of
"application_context": {
"return_url": "http://localhost:8081/process",
"cancel_url": "http://localhost:8081/cancel",
"locale": "en-US",
"landing_page": "BILLING",
"user_action": "CONTINUE"
},
return url ( /process route for success ), the request DOES NOT contain payment_id, only PAYER_ID. But the payment_id ( or order_id in v2 ) is needed to capture the order.
Since i have found literally zero blogs, tutorials, guide for v2 ( only millions for v1) i am confused where to get the order id. Do i really need to save it in DB after i create the order? OR is there any other trick?
Also, the button contains onApprove method, but after creating order, the browser is redirected to paypal, and the paypal redirects user to http://localhost:8081/process endpoint - thus the onApprove method is never invoked and useless (?).
This whole flow of v2 is really confusing, is there something i am missing?
Thanks for help
With your v2 code, do not use any redirects. At all. Your button should call 2 endpoints on your server. These two endpoints should (respectively) do the API operations of creating and capturing the order, and return the JSON result in each case (the capture route can do any server-side operations like storing the transaction result in the database before forwarding the JSON result to the client caller, since the client needs to handle any capture error situations as well as showing a success message). You can find a full stack node.js example in the PayPal integration guide, but it's fine to keep your #paypal/react-paypal-js code pretty much as-is for the front end.

Historian for a particular participant

Is there any way in which I can get Historian for a particular participant in hyperledger-composer using node API?
I am developing an application based on hyperledger-composer using Node APIs.I want to show the history of transaction of a particular participant in his/her profile. I have created the permission.acl for that and that is working fine in playground. But when i am accessing the historian from node API it is giving complete historian of the network. I don't know how to filter that for a participant.
you can return results from REST API calls since v0.20 to the calling client application, so something like the following would work (not tested, but you get the idea). NOTE: You could just call the REST API end (/GET Trader) direct via REST with your parameter (or whatever endpoints you create for your own business network - the example below is trade-network), rather than the example of using 'READ-ONLY' Transaction processor Endpoint described below, for returning larger result sets to your client application. See more on this in the docs
NODE JS Client using APIs:
const BusinessNetworkConnection = require('composer-client').BusinessNetworkConnection;
const rp = require('request-promise');
this.bizNetworkConnection = new BusinessNetworkConnection();
this.cardName ='admin#mynet';
this.businessNetworkIdentifier = 'mynet';
this.bizNetworkConnection.connect(this.cardName)
.then((result) => {
//You can do ANYTHING HERE eg.
})
.catch((error) => {
throw error;
});
// set up my read only transaction object - find the history of a particular Participant - note it could equally be an Asset instead !
var obj = {
"$class": "org.example.trading.MyPartHistory",
"tradeId": "P1"
};
async function callPartHistory() {
var options = {
method: 'POST',
uri: 'http://localhost:3000/api/MyPartHistory',
body: obj,
json: true
};
let results = await rp(options);
// console.log("Return value from REST API is " + results);
console.log(" ");
console.log(`PARTICIPANT HISTORY for Asset ID: ${results[0].tradeId} is: `);
console.log("=============================================");
for (const part of results) {
console.log(`${part.tradeId} ${part.name}` );
}
}
// Main
callPartHistory();
//
MODEL FILE
#commit(false)
#returns(Trader[])
transaction MyPartHistory {
o String tradeId
}
READ-ONLY TRANSACTION PROCESSOR CODE (in 'logic.js') :
/**
* Sample read-only transaction
* #param {org.example.trading.MyPartHistory} tx
* #returns {org.example.trading.Trader[]} All trxns
* #transaction
*/
async function participantHistory(tx) {
const partId = tx.tradeid;
const nativeSupport = tx.nativeSupport;
// const partRegistry = await getParticipantRegistry('org.example.trading.Trader')
const nativeKey = getNativeAPI().createCompositeKey('Asset:org.example.trading.Trader', [partId]);
const iterator = await getNativeAPI().getHistoryForKey(nativeKey);
let results = [];
let res = {done : false};
while (!res.done) {
res = await iterator.next();
if (res && res.value && res.value.value) {
let val = res.value.value.toString('utf8');
if (val.length > 0) {
console.log("#debug val is " + val );
results.push(JSON.parse(val));
}
}
if (res && res.done) {
try {
iterator.close();
}
catch (err) {
}
}
}
var newArray = [];
for (const item of results) {
newArray.push(getSerializer().fromJSON(item));
}
console.log("#debug the results to be returned are as follows: ");
return newArray; // returns something to my NodeJS client (called via REST API)
}

Send push notifications in nodejs using fcm node module

I want to send push notifications on a device I have device ID , how can I send notifications on that device.
I have used FCM to send the notifications to the users. I used Mongodb,Nodejs and fcm-push node module.
Please see the codes, Hope it will help you.
deviceModel.js :
var mongoose = require('mongoose');
var Schema = require('mongoose').Schema;
var schema = new Schema({
userId: { type: Schema.Types.ObjectId },
userType: { type: String },
deviceType: { type: String, required: true, enum: ['apn', 'gcm'] },
deviceId: { type: String, required: true },
timestamp: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Device', schema);
push.js:
var pushModule = require('./push');
var DeviceModel = require('./DeviceModel');
var highland = require('highland');
var FCM = require('fcm-push');
var serverKey = config.get('fcmServerKey');
var fcm = new FCM(serverKey);
var _ = require('lodash');
/**
* Handle fcm push results and updates/invalidates device ids.
*
* #param {String[]} deviceIds - array of device ids used in send.
* #param {Object} fcmResponse - fcm send response.
*/
function handleFcmSendResult(deviceIds, fcmResponse) {
// fcm results
var results = fcmResponse.results;
// changes for deviceIds
var fcmUpdated = [];
var fcmDeleted = [];
// process results
for (var i = 0; i < results.length; i++) {
var oldId = deviceIds[i];
var deviceResult = results[i];
var msgId = deviceResult.message_id;
var newId = deviceResult.registration_id;
if (_.isString(msgId) && _.isString(newId)) {
// If registration_id is set, replace the original ID with the new value
fcmUpdated.push({ oldId: oldId, newId: newId });
} else {
// Otherwise, get the value of error
var e = deviceResult.error;
if (e === 'Unavailable') {
winston.warn('Push: FCM: Feedback: device unavailable: %s.', oldId);
} else if (e === 'NotRegistered' || e === 'InvalidRegistration') {
// delete invalid devices
fcmDeleted.push(oldId);
}
}
}
// apply changes, in bulk
var bulkOp = DeviceModel.collection.initializeUnorderedBulkOp();
if (fcmUpdated.length > 0) {
fcmUpdated.forEach(function (upd) {
bulkOp.find({ deviceId: upd.oldId }).update({ deviceId: upd.newId, timestamp: Date.now() });
});
// those old ids that are updated, need not be deleted
fcmDeleted = _.difference(fcmDeleted, _.map(fcmUpdated, _.property('oldId')));
}
if (fcmDeleted.length > 0) {
bulkOp.find({ deviceId: { '$in': fcmDeleted } }).remove();
}
console.log(bulkOp);
// run bulk op
bulkOp.execute();
}
/**
* Dispatch FCM push to device ids.
*
* #param {String[]} deviceIds - array of apn device ids.
* #param {String} eventName - event name.
* #param {*} eventData - event data.
*/
function sendFcm(deviceIds, eventName, eventData) {
// payload
var msgOpts = {
priority: 'high',
registration_ids: deviceIds,
data: _.set(eventData, 'eventName', eventName),
notification: eventData,
content_available: true,
mutable_content: true
};
fcm.send(msgOpts)
.then(function (response) {
console.log("SENT :",response);
// handleFcmSendResult(deviceIds, JSON.parse(response));
})
.catch(function (err) {
winston.error('Push: FCM: Error sending push.', err);
})
}
/**
* Sends push notifications to Device docs emitted from stream.
*
* #param {Stream.Readable} docStream - Stream of device docs.
* #param {String} eventName - event name.
* #param {*} eventData - event data.
*/
function streamSend(docStream, eventName, eventData) {
// stream for fcm
var fcmStream = highland();
// batch device ids from sub stream and sent to gcm
fcmStream.batch(1000).each(function (fcmIds) {
sendFcm(fcmIds, eventName, eventData);
});
// split source to sub streams
highland(docStream).each(function (doc) {
fcmStream.write(doc.deviceId);
}).done(function () {
// end sub streams when doc source is done
fcmStream.end();
});
}
/**
* Sends the event via push to all registered devices.
* #param {String} eventName - event name.
* #param {Object} eventData - event data. Can contain a "notification" object with: title, description and icon.
*/
var pushToPublic = function (eventName, eventData) {
var str = DeviceModel.find().cursor();
streamSend(str, eventName, eventData);
}
/**
* Sends the event via push to devices that are mapped to given user ids.
* #param {ObjectId[]} userIds - array of user ids.
* #param {String} eventName - event name.
* #param {Object} eventData - event data. Can contain a "notification" object with: title, description and icon.
*/
var pushToUserIds = function (userIds, eventName, eventData) {
var str = DeviceModel.find({ userId: { '$in': userIds } }).cursor();
streamSend(str, eventName, eventData);
}
// Send notification test function
var sendNotification = function () {
var payload = {
"updatedAt": "2017-06-17T06:12:42.975Z",
"message": "this is notification message",
"typeId": "591452ecad4c6b71bed61089",
"userId": "5912d45f29945b6d649f287e",
"_id": "5913f90d08b4d213f1ded021",
"isRead": false,
"isPublic": true,
// ORDER DELIVERD
"type": "order",
"title_loc_key": "title_order_delivered",
"title_loc_args": ["OrderValue"],
"body_loc_key": "body_order_delivered",
"body_loc_args": ["reminderValue"],
};
// pushToPublic("testEvent", payload);
pushToUserIds(['59562201a544614d47845eef'], "testEvent", payload)
}
sendNotification();
Result:
SENT:{ "multicast_id":1234567891234567890,
"success":1,
"failure":0,
"canonical_ids":0,
"results":[{
"message_id": "0:12345678912345678912345678912345678"
}]
}
you can use nodejs package node-gcm
for more please check this link will you more and solve your problem .
node package1
or
node package2

AWS X-Ray w/ HAPI - No logs in Console

Trying out AWS new service X-RAY for my microservice deployment on Kubernetes on EC2. Here are the logs I am seeing:
root#internal-reporting-test-1680758663-3gweh:/usr/src/app# cat AWSXRay.log
2016-12-06 04:36:23.5840 +00:00 [INFO] UDP message sent: {"trace_id":"1-58464048-03fbcdf5c861f95fc5204ec6","id":"105107e6c9df4bfe","start_time":1480998983.553,"name":"localhost","http":{"request":{"method":"GET","user_agent":"curl/7.38.0","client_ip":"127.0.0.1","url":"http://localhost"},"response":{"status":404,"content_length":0}},"service":{"version":"1.0.0"},"error":true,"end_time":1480998983.574}
2016-12-06 04:36:33.2560 +00:00 [INFO] UDP message sent: {"trace_id":"1-58464051-3e3799a566c0933093f51b7a","id":"94d4267609d2ba40","start_time":1480998993.205,"name":"localhost","http":{"request":{"method":"GET","user_agent":"curl/7.38.0","client_ip":"127.0.0.1","url":"http://localhost"},"response":{"status":200,"content_length":0}},"service":{"version":"1.0.0"},"end_time":1480998993.252}
2016-12-06 04:36:35.5810 +00:00 [INFO] UDP message sent: {"trace_id":"1-58464054-5df1e9ec46ef2b26b2d29965","id":"113fd36cddb5de95","start_time":1480998995.552,"name":"localhost","http":{"request":{"method":"GET","user_agent":"curl/7.38.0","client_ip":"127.0.0.1","url":"http://localhost"},"response":{"status":200,"content_length":0}},"service":{"version":"1.0.0"},"end_time":1480998995.579}
2016-12-06 04:36:41.3350 +00:00 [INFO] UDP message sent: {"trace_id":"1-58464059-0dfd2e274ca20499c6bce17f","id":"1dfac818142b2d28","start_time":1480999001.304,"name":"localhost","http":{"request":{"method":"GET","user_agent":"curl/7.38.0","client_ip":"127.0.0.1","url":"http://localhost"},"response":{"status":200,"content_length":0}},"service":{"version":"1.0.0"},"end_time":1480999001.332}
Curl I am using:
# curl localhost/v1/internal/report/users
Supervisor Conf:
[supervisord]
nodaemon=true
[program:xray]
command=/usr/src/app/xray
[program:npm]
command=npm start
Mods to express middleware to use with hapi:
/**
* Express_mw module.
*
* Exposes middleware functions to enable automated data capturing on a web service. To enable on a Node.js/Express application,
* use 'app.use(AWSXRay.express.openSegment())' before defining your routes. After your routes, before any extra error
* handling middleware, use 'app.use(AWSXRay.express.closeSegment())'.
* Use AWSXRay.getSegment() to access the current segment/subsegment.
* Otherwise, for manual mode, this appends the Segment object to the request object as req.segment.
* #module express_mw
*/
var _ = require('underscore');
var CLSUtils = require('../utils').CLSUtils;
var MWUtils = require('./mw_utils');
var Local = require('../segments/attributes/local');
var Segment = require('../segments/segment');
var getCauseType = require('../utils').getCauseTypeFromHttpStatus;
var XRAY_HEADER = 'x-amzn-trace-id';
var NAME = process.env.XRAY_TRACING_NAME;
var DEFAULT_NAME = process.env.XRAY_TRACING_DEFAULT_NAME;
/**
* Use 'app.use(AWSXRay.express.openSegment())' before defining your routes.
* Use AWSXRay.getSegment() to access the current segment/subsegment.
* Otherwise, for manual mode, this appends the Segment object to the request object as req.segment.
* #alias module:express_mw.openSegment
* #returns {function}
*/
module.exports.openSegment = function openSegment() {
if (_.isUndefined(DEFAULT_NAME) && _.isUndefined(MWUtils.defaultName)) {
throw new Error('Default segment name must be set using "process.env.XRAY_TRACING_DEFAULT_NAME"' +
' or "setDefaultName()".');
}
return function open(request, next) {
/*
* Get the true HTTP objects
*/
req = request.raw.req;
res = request.raw.res;
var amznTraceHeader = processHeaders(req);
var segment = new Segment(resolveName(req.headers.host), amznTraceHeader.Root, amznTraceHeader.Parent);
resolveSampling(amznTraceHeader, segment, res);
segment.addAttribute('http', new Local(req));
MWUtils.populate(segment);
res.on('finish', function () {
if (this.statusCode === 429)
segment.addThrottle();
else if (getCauseType(this.statusCode))
segment[getCauseType(this.statusCode)] = true;
segment.http.close(this);
segment.close();
});
if (CLSUtils.isCLSMode()) {
var ns = CLSUtils.getNamespace();
ns.bindEmitter(req);
ns.bindEmitter(res);
ns.run(function () {
CLSUtils.setSegment(segment);
if (next) { next(); }
});
} else {
req.segment = segment;
if (next) { next(); }
}
};
};
/**
* After your routes, before any extra error handling middleware, use 'app.use(AWSXRay.express.closeSegment())'.
* #alias module:express_mw.closeSegment
* #returns {function}
*/
module.exports.closeSegment = function closeSegment() {
return function close(request, next) {
req = request.raw.req;
res = request.raw.res;
var segment = CLSUtils.isCLSMode() ? CLSUtils.getSegment() : segment = req.segment;
if (segment && err) {
segment.counter = 0;
segment.close(err);
} else if (segment) {
segment.close();
}
if (next)
next(err);
};
};
function processHeaders(req) {
var amznTraceHeader = {};
if (req && req.headers && req.headers[XRAY_HEADER]) {
_.each(req.headers[XRAY_HEADER].replace(/ /g,'').split(';'), function (header) {
var pair = header.split('=');
this[pair[0]] = pair[1];
}, amznTraceHeader);
}
return amznTraceHeader;
}
function resolveName(hostName) {
if (NAME)
return NAME;
var regex = new RegExp('(?:[0-9]{1,3}\.){3}[0-9]{1,3}', 'g');
var hostIp = hostName.match(regex) || _.isEmpty(hostName);
return !hostIp ? hostName : (DEFAULT_NAME ? DEFAULT_NAME : MWUtils.defaultName);
}
function resolveSampling(amznTraceHeader, segment, res) {
var isSampled;
if (amznTraceHeader.Sampled === '1')
isSampled = true;
else if (amznTraceHeader.Sampled === '0')
isSampled = false;
isSampled = !_.isUndefined(isSampled) ? isSampled : MWUtils.shouldSample();
if (amznTraceHeader.Sampled === '?')
res.header[XRAY_HEADER] = 'Root=' + amznTraceHeader.Root + '; Sampled=' + (isSampled ? '1' : '0');
if (!isSampled)
segment.notTraced = true;
}
The changes are relatively simple while playing with this service. Just pretty much forcing the raw node HTTP objects to the middleware.
Logs are showing the UDP requests to the xray service are working as expected. Yet in the console I see no results.
Anyone have any ideas?

SmoothStreaming issues on Chromecast

I'm trying to load a smoothstreaming media into the chromecast. For that I've used the samples provided by google:
<body>
<video id='vid' />
<script type="text/javascript"
src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js">
</script>
<script type="text/javascript"
src="//www.gstatic.com/cast/sdk/libs/mediaplayer/0.3.0/media_player.js">
</script>
<script type="text/javascript">
window.onload = function() {
// If you set ?Debug=true in the URL, such as a different App ID in the
// developer console, include debugging information.
cast.receiver.logger.setLevelValue(cast.receiver.LoggerLevel.DEBUG);
var mediaElement = document.getElementById('vid');
// Create the media manager. This will handle all media messages by default.
window.mediaManager = new cast.receiver.MediaManager(mediaElement);
// Remember the default value for the Receiver onLoad, so this sample can Play
// non-adaptive media as well.
window.defaultOnLoad = mediaManager.onLoad;
mediaManager.onLoad = function (event) {
/proto
// MPEG-DASH
protocol = cast.player.api.CreateDashStreamingProtocol(host);
} else if (url.indexOf('.ism/') >= 0) {
// Smooth Streaming
protocol = cast.player.api.CreateSmoothStreamingProtocol(host);
}
// How to override a method in Host. I know that it's safe to just provide this
// method.
host.onError = function(errorCode) {
console.log("Fatal Error - "+errorCode);
window.player.unload();
};
// If you need cookies, then set withCredentials = true also set any header
// information you need. If you don't need them, there can be some unexpected
// effects by setting this value.
// host.updateSegmentRequestInfo = function(requestInfo) {
// requestInfo.withCredentials = true;
// };
console.log("we have protocol "+ext);
if (protocol !== null) {
console.log("Starting Media Player Library");
window.player = new cast.player.api.Player(host);
window.player.load(protocol, initStart);
}
mediaElement.autoplay = autoplay; // Make sure autoplay get's set
if (url.lastIndexOf('.m3u8') >= 0) {
// HTTP Live Streaming
protocol = cast.player.api.CreateHlsStreamingProtocol(host);
} else if (url.lastIndexOf('.mpd') >= 0) {
// MPEG-DASH
protocol = cast.player.api.CreateDashStreamingProtocol(host);
} else if (url.indexOf('.ism/') >= 0) {
// Smooth Streaming
protocol = cast.player.api.CreateSmoothStreamingProtocol(host);
}
// How to override a method in Host. I know that it's safe to just provide this
// method.
host.onError = function(errorCode) {
console.log("Fatal Error - "+errorCode);
window.player.unload();
};
// If you need cookies, then set withCredentials = true also set any header
// information you need. If you don't need them, there can be some unexpected
// effects by setting this value.
// host.updateSegmentRequestInfo = function(requestInfo) {
// requestInfo.withCredentials = true;
// };
console.log("we have protocol "+ext);
And on the sender:
/**
* global variables
*/
var currentMediaSession = null;
var currentVolume = 0.5;
var progressFlag = 1;
var mediaCurrentTime = 0;
var session = null;
var mediaURLs = [
'http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/',
'http://commondatastorage.googleapis.com/gtv-videos-bucket/ED_1280.mp4',
'http://commondatastorage.googleapis.com/gtv-videos-bucket/tears_of_steel_1080p.mov',
'http://commondatastorage.googleapis.com/gtv-videos-bucket/reel_2012_1280x720.mp4',
'http://commondatastorage.googleapis.com/gtv-videos-bucket/Google%20IO%202011%2045%20Min%20Walk%20Out.mp3'];
var mediaTitles = [
'Big Buck Bunny',
'Elephant Dream',
'Tears of Steel',
'Reel 2012',
'Google I/O 2011 Audio'];
var mediaThumbs = [
'images/bunny.jpg',
'images/ed.jpg',
'images/Tears.jpg',
'images/reel.jpg',
'images/google-io-2011.jpg'];
var currentMediaURL = mediaURLs[0];
/**
* Call initialization
*/
if (!chrome.cast || !chrome.cast.isAvailable) {
setTimeout(initializeCastApi, 1000);
}
/**
* initialization
*/
function initializeCastApi() {
// default app ID to the default media receiver app
// optional: you may change it to your own app ID/receiver
var applicationID = '21176C05';
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
sessionListener,
receiverListener);
chrome.cast.initialize(apiConfig, onInitSuccess, onError);
};
/**
* initialization success callback
*/
function onInitSuccess() {
appendMessage("init success");
}
/**
* initialization error callback
*/
function onError() {
console.log("error");
appendMessage("error");
}
/**
* generic success callback
*/
function onSuccess(message) {
console.log(message);
}
/**
* callback on success for stopping app
*/
function onStopAppSuccess() {
console.log('Session stopped');
appendMessage('Session stopped');
document.getElementById("casticon").src = 'images/cast_icon_idle.png';
}
/**
* session listener during initialization
*/
function sessionListener(e) {
console.log('New session ID: ' + e.sessionId);
appendMessage('New session ID:' + e.sessionId);
session = e;
if (session.media.length != 0) {
appendMessage(
'Found ' + session.media.length + ' existing media sessions.');
onMediaDiscovered('onRequestSessionSuccess_', session.media[0]);
}
session.addMediaListener(
onMediaDiscovered.bind(this, 'addMediaListener'));
session.addUpdateListener(sessionUpdateListener.bind(this));
}
/**
* session update listener
*/
function sessionUpdateListener(isAlive) {
var message = isAlive ? 'Session Updated' : 'Session Removed';
message += ': ' + session.sessionId;
appendMessage(message);
if (!isAlive) {
session = null;
}
};
/**
* receiver listener during initialization
*/
function receiverListener(e) {
if( e === 'available' ) {
console.log("receiver found");
appendMessage("receiver found");
}
else {
console.log("receiver list empty");
appendMessage("receiver list empty");
}
}
/**
* select a media URL
* #param {string} m An index for media URL
*/
function selectMedia(m) {
console.log("media selected" + m);
appendMessage("media selected" + m);
currentMediaURL = mediaURLs[m];
var playpauseresume = document.getElementById("playpauseresume");
document.getElementById('thumb').src = mediaThumbs[m];
}
/**
* launch app and request session
*/
function launchApp() {
console.log("launching app...");
appendMessage("launching app...");
chrome.cast.requestSession(onRequestSessionSuccess, onLaunchError);
}
/**
* callback on success for requestSession call
* #param {Object} e A non-null new session.
*/
function onRequestSessionSuccess(e) {
console.log("session success: " + e.sessionId);
appendMessage("session success: " + e.sessionId);
session = e;
document.getElementById("casticon").src = 'images/cast_icon_active.png';
}
/**
* callback on launch error
*/
function onLaunchError() {
console.log("launch error");
appendMessage("launch error");
}
/**
* stop app/session
*/
function stopApp() {
session.stop(onStopAppSuccess, onError);
}
/**
* load media
* #param {string} i An index for media
*/
function loadMedia(i) {
if (!session) {
console.log("no session");
appendMessage("no session");
return;
}
console.log("loading..." + currentMediaURL);
appendMessage("loading..." + currentMediaURL);
var mediaInfo = new chrome.cast.media.MediaInfo(currentMediaURL);
mediaInfo.contentType = 'application/vnd.ms-sstr+xml';
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.autoplay = false;
request.currentTime = 0;
var payload = {
"title:" : mediaTitles[i],
"thumb" : mediaThumbs[i]
};
var json = {
"payload" : payload
};
request.customData = json;
session.loadMedia(request,
onMediaDiscovered.bind(this, 'loadMedia'),
onMediaError);
}
/**
* callback on success for loading media
* #param {Object} e A non-null media object
*/
function onMediaDiscovered(how, mediaSession) {
console.log("new media session ID:" + mediaSession.mediaSessionId);
appendMessage("new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')');
currentMediaSession = mediaSession;
mediaSession.addUpdateListener(onMediaStatusUpdate);
mediaCurrentTime = currentMediaSession.currentTime;
playpauseresume.innerHTML = 'Play';
document.getElementById("casticon").src = 'images/cast_icon_active.png';
}
/**
* callback on media loading error
* #param {Object} e A non-null media object
*/
function onMediaError(e) {
console.log("media error");
appendMessage("media error");
document.getElementById("casticon").src = 'images/cast_icon_warning.png';
}
/**
* callback for media status event
* #param {Object} e A non-null media object
*/
function onMediaStatusUpdate(isAlive) {
if( progressFlag ) {
document.getElementById("progress").value = parseInt(100 * currentMediaSession.currentTime / currentMediaSession.media.duration);
}
document.getElementById("playerstate").innerHTML = currentMediaSession.playerState;
}
/**
* play media
*/
function playMedia() {
if( !currentMediaSession )
return;
var playpauseresume = document.getElementById("playpauseresume");
if( playpauseresume.innerHTML == 'Play' ) {
currentMediaSession.play(null,
mediaCommandSuccessCallback.bind(this,"playing started for " + currentMediaSession.sessionId),
onError);
playpauseresume.innerHTML = 'Pause';
//currentMediaSession.addListener(onMediaStatusUpdate);
appendMessage("play started");
}
else {
if( playpauseresume.innerHTML == 'Pause' ) {
currentMediaSession.pause(null,
mediaCommandSuccessCallback.bind(this,"paused " + currentMediaSession.sessionId),
onError);
playpauseresume.innerHTML = 'Resume';
appendMessage("paused");
}
else {
if( playpauseresume.innerHTML == 'Resume' ) {
currentMediaSession.play(null,
mediaCommandSuccessCallback.bind(this,"resumed " + currentMediaSession.sessionId),
onError);
playpauseresume.innerHTML = 'Pause';
appendMessage("resumed");
}
}
}
}
/**
* stop media
*/
function stopMedia() {
if( !currentMediaSession )
return;
currentMediaSession.stop(null,
mediaCommandSuccessCallback.bind(this,"stopped " + currentMediaSession.sessionId),
onError);
var playpauseresume = document.getElementById("playpauseresume");
playpauseresume.innerHTML = 'Play';
appendMessage("media stopped");
}
/**
* set media volume
* #param {Number} level A number for volume level
* #param {Boolean} mute A true/false for mute/unmute
*/
function setMediaVolume(level, mute) {
if( !currentMediaSession )
return;
var volume = new chrome.cast.Volume();
volume.level = level;
currentVolume = volume.level;
volume.muted = mute;
var request = new chrome.cast.media.VolumeRequest();
request.volume = volume;
currentMediaSession.setVolume(request,
mediaCommandSuccessCallback.bind(this, 'media set-volume done'),
onError);
}
/**
* mute media
* #param {DOM Object} cb A checkbox element
*/
function muteMedia(cb) {
if( cb.checked == true ) {
document.getElementById('muteText').innerHTML = 'Unmute media';
setMediaVolume(currentVolume, true);
appendMessage("media muted");
}
else {
document.getElementById('muteText').innerHTML = 'Mute media';
setMediaVolume(currentVolume, false);
appendMessage("media unmuted");
}
}
/**
* seek media position
* #param {Number} pos A number to indicate percent
*/
function seekMedia(pos) {
console.log('Seeking ' + currentMediaSession.sessionId + ':' +
currentMediaSession.mediaSessionId + ' to ' + pos + "%");
progressFlag = 0;
var request = new chrome.cast.media.SeekRequest();
request.currentTime = pos * currentMediaSession.media.duration / 100;
currentMediaSession.seek(request,
onSeekSuccess.bind(this, 'media seek done'),
onError);
}
/**
* callback on success for media commands
* #param {string} info A message string
* #param {Object} e A non-null media object
*/
function onSeekSuccess(info) {
console.log(info);
appendMessage(info);
setTimeout(function(){progressFlag = 1},1500);
}
/**
* callback on success for media commands
* #param {string} info A message string
* #param {Object} e A non-null media object
*/
function mediaCommandSuccessCallback(info) {
console.log(info);
appendMessage(info);
}
/**
* append message to debug message window
* #param {string} message A message string
*/
function appendMessage(message) {
var dw = document.getElementById("debugmessage");
dw.innerHTML += '\n' + JSON.stringify(message);
};
I am trying media #1 which is .ism - I also tried with the /Manifest - but no luck.
In the debugging console I am getting:
[214.054s] [cast.player.api.Player] load -3
media_player.js:23
[214.060s] [goog.net.XhrIo] Opening Xhr [GET http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest -1]
media_player.js:23
[214.065s] [goog.net.XhrIo] Will abort after 10000ms if incomplete, xhr2 false [GET http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest -1]
media_player.js:23
[214.070s] [goog.net.XhrIo] Sending request [GET http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest -1]
media_player.js:23
[214.088s] [goog.net.XhrIo] Request complete [GET http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest 200]
media_player.js:23
[214.120s] [cast.player.api.Player] sourceopen
media_player.js:23
[214.142s] [cast.player.api.Player] play
media_player.js:23
[214.291s] [cast.receiver.MediaManager] Load metadata error
cast_receiver.js:19
[214.296s] [cast.receiver.MediaManager] Resetting media element
cast_receiver.js:19
[214.303s] [cast.receiver.MediaManager] Sending error message to 4:client-34217
cast_receiver.js:19
[214.307s] [cast.receiver.IpcChannel] IPC message sent: {"namespace":"urn:x-cast:com.google.cast.media","senderId":"4:client-34217","data":"{\"requestId\":9217017,\"type\":\"LOAD_FAILED\"}"}
cast_receiver.js:19
[214.171s] [cast.player.api.Player] error
media_player.js:23
Fatal Error - 1 index.html:61
[214.176s] [cast.player.api.Player] unload -3
Any idea???
Thanks!
Where did you find that URL? It's incomplete, the right one is http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/manifest, but it is unlikely to work. Other files have been posted at: Microsoft's SmoothStreaming samples files, that said, I don't believe that Microsoft is providing a CORS header that works for Chromecast.
We do provide some very simple media that you can host yourself. And we provide a very simple server, if you need that to host and provide a CORS header as well.

Resources