I'm setting up push notification using the outlook api, the server is written in nodejs. This is the post request the client makes for the subscription
POST /api/v2.0/me/subscriptions HTTP/1.1
Host: outlook.office.com
Content-Type: application/json
client-request-id: f7d3812g-dfbz-4eb5-8edg-0f9a3ad716aq
User-Agent: node-outlook/2.0
return-client-request-id: true
X-Anchor-Mailbox: bill_gates#outlook.com
Authorization: Bearer "ACCESS_TOKEN"
Content-Type: application/json
{
"#odata.type":"#Microsoft.OutlookServices.PushSubscription",
"Resource": "https://outlook.office.com/api/v2.0/me/events",
"NotificationURL": "https://mywebsite.azurewebsites.net/push",
"ChangeType": "Created,Deleted,Updated"
}
The nodejs server then responds with the validation token that was generated by the outlook notification service
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.write(validation_token);
response.end();
The client (which sent the original post request) then receives the following response
{
"#odata.context":"https://outlook.office.com/api/v2.0/$metadata#Me/Subscription/$entity",
"#odata.type": "#Microsoft.OutlookServices.PushSubscription",
"#odata.id": "https://outlook.office.com/api/v2.0/Users('00034001-ffef-f16e-0000-000000000000#74df9q7f-e9s6-40ad-b43w-aaaaaaaaaaaa')/Subscriptions('RERFNkJFNGUsNEE1My00RjFFLUExQkMtQkU1NkQ9OTdDOTlBXzAwMDM0MDAxLUZGRUYtZTE2RS0wMDAwLTAwMDAwMDAwMEAwMA==')",
"Id": "RERFNkJFNGUsNEE1My00RjFFLUExQkMtQkU1NkQ9OTdDOTlBXzAwMDM0MDAxLUZGRUYtZTE2RS0wMDAwLTAwMDAwMDAwMEAwMA==",
"Resource": "https://outlook.office.com/api/v2.0/me/messages",
"ChangeType": "Created, Updated, Deleted, Missed",
"NotificationURL": "https://mywebsite.azurewebsites.net/push",
"SubscriptionExpirationDateTime": "2017-03-30T09:35:37.6586596Z",
"ClientState": null
}
This is exactly what the outlook documentation states should happen https://dev.outlook.com/restapi/concepts/webhooks
This process validates the NotificationURL to receive push notifications from outlook. The NotificationURL end point is receiving a response from outlook when an event is created, but not the one I want!!!
I should expect to receive something like this:
{
"value": [
{
"#odata.type": "#Microsoft.OutlookServices.Notification",
"Id": null,
"SubscriptionId": "Mjk3QNERDQQ==",
"SubscriptionExpirationDateTime": "2015-04-23T22:46:13.8805047Z",
"SequenceNumber": 1,
"ChangeType": "Created",
"Resource" : "https://outlook.office.com/api/v2.0/Users('ddfcd489-628b-7d04-b48b-20075df800e5#1717622f-1d94-c0d4-9d74-f907ad6677b4')/Events('AAMkADNkNmAA=')",
"ResourceData": {
"#odata.type": "#Microsoft.OutlookServices.Event",
"#odata.id": "https://outlook.office.com/api/v2.0/Users('ddfcd489-628b-7d04-b48b-20075df800e5#1717622f-1d94-c0d4-9d74-f907ad6677b4')/Events('AAMkADNkNmAA=')",
"Id": "AAMkADNkNmAA="
}
}
]
}
But instead I'm receiving something like this
{
_readableState:
ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: null,
pipesCount: 0,
flowing: null,
ended: false,
endEmitted: false,
reading: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
defaultEncoding: 'utf8',
ranOut: false,
awaitDrain: 0,
readingMore: true,
decoder: null,
encoding: null }, .......
And this response body goes on for another 600 lines.
I know there is a lot going on there, but any tips or help would be greatly appreciated.
I've just found the solution. When I am reading the request from the outlook notification, I have to switch the stream flowing to on, by default this was set to null (see the ReadableState JSON above).
It's very simple, just chain on to the request body, and use a callback.
More information can be found here: https://www.sandersdenardi.com/readable-writable-transform-streams-node/
So this becomes
request.on('data', function(record) {
console.log('received: ' + record);
});
Related
When using a form to upload some files I can see in dev tools in the network inspector and specifically in the payload tab under form data, in view source.
Note the below includes the file name including the path twoItems/Screenshot... its this path twoItems I need to access in the API but can't.
Security? Err why do I want this?
It's for a document management app, users cant be creating folders in the browser and then add the files. They need to drag and drop nested directories of files.
------WebKitFormBoundarydJ6knkAHgNW7SIF7
Content-Disposition: form-data; name="file"; filename="twoItems/Screenshot 2022-03-11 at 08.58.24.png"
Content-Type: image/png
------WebKitFormBoundarydJ6knkAHgNW7SIF7
Content-Disposition: form-data; name="file"; filename="twoItems/Screenshot 2022-03-11 at 08.58.08.png"
Content-Type: image/png
so in the API I have a standard fastify API running
mport Fastify, { FastifyInstance, RouteShorthandOptions } from "fastify";
import { Server, IncomingMessage, ServerResponse } from "http";
const fs = require("fs");
const util = require("util");
const { pipeline } = require("stream");
const pump = util.promisify(pipeline);
const fastify: FastifyInstance = Fastify({});
fastify.register(require("fastify-multipart"));
fastify.register(require("fastify-cors"), {
methods: ["GET", "PUT", "POST"],
});
const dir = "./files";
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
fastify.post("/upload", async (req: any, reply) => {
console.log(req);
const parts = await req.files();
for await (const part of parts) {
console.log(part); //---------------- LOG BELOW
await pump(part.file, fs.createWriteStream(`./files/${part.filename}`));
}
reply.send();
});
const start = async () => {
try {
await fastify.listen(3001);
const address = fastify.server.address();
const port = typeof address === "string" ? address : address?.port;
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();
I can't find how to access the path of each item
when I log out part I get...
<ref *1> {
fieldname: 'file',
filename: 'Screenshot 2022-03-11 at 17.52.11.png',
encoding: '7bit',
mimetype: 'image/png',
file: FileStream {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: [Object], tail: [Object], length: 4 },
length: 208151,
pipes: [],
flowing: null,
ended: false,
endEmitted: false,
reading: false,
sync: false,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
dataEmitted: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: null
},
_events: [Object: null prototype] {
end: [Function (anonymous)],
limit: [Function (anonymous)]
},
_eventsCount: 2,
_maxListeners: undefined,
bytesRead: 208151,
truncated: false,
_read: [Function (anonymous)],
[Symbol(kCapture)]: false
},
fields: { file: [ [Object], [Object], [Object], [Circular *1] ] },
_buf: null,
toBuffer: [AsyncFunction: toBuffer]
}
this is undefined...
console.log(part.path);
You need to set the busboy's option:
fastify.register(require("fastify-multipart"), {
preservePath: true
});
You can find all the options here: https://github.com/fastify/busboy#busboy-methods
Braintree provides an api to search for the transactions. Braintree has provided an example, but i'm not aware how to read the node stream returned by braintree. Please look into the following code snippet:
var stream = gateway.transaction.search(function (search) {
search.paymentMethodToken().is("h337xg");
});
stream.pipe(someWritableStream);
//When I try to print the stream in console, I get the following result:
{
_readableState:
{ highWaterMark: 16384,
buffer: [],
length: 0,
pipes: null,
pipesCount: 0,
flowing: false,
ended: false,
endEmitted: false,
reading: false,
calledRead: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
objectMode: true,
defaultEncoding: 'utf8',
ranOut: false,
awaitDrain: 0,
readingMore: false,
decoder: null,
encoding: null },
readable: true,
domain: null,
_events: {},
_maxListeners: 10,
searchResponse: { stream: [Circular], success: true },
currentItem: 0,
currentOffset: 0,
bufferedResults: []
}
From nodejs stream documentation
http://nodejs.org/api/stream.html#apicontent
A stream is an abstract interface implemented by various objects in Node. For >example a request to an HTTP server is a stream, as is stdout. Streams are >readable, writable, or both. All streams are instances of EventEmitter
You should make use of stream's data event to capture data as it is being received by the stream. The stream's end event is called when complete data is received from the steam
completeData = ""
someWritableStream.on("data", function(chunk){
//Do Something With the chunk of data. You might want to concat the stream
completeData += chunk;
});
someWritableStream.on("end", function(){
//Do Something after the all the chunks are received.
console.log(completeData);
});
I am trying to build an api route for a transaction, I want to grab the transaction details from the braintree server.
My app is setup so that the braintree customerId() is the same as the ID as my app user. So below I search for all the transactions for a particular user logged in.
Meaning req.user.id will always equal customerId() which sits on the braintree server.
app.get('/project', function(req, res) {
if(req.user) {
// Finds all transactions for a particular customer
var stream = gateway.transaction.search(function (search) {
search.customerId().is(req.user.id);
});
console.log(stream);
//res.send(stream);
}
});
I want to return the details, but the stream looks like below. So obviously this makes me think that there is a certain way to handle a node stream?
I have seen things like readable-stream, I am not sure if that is what I need to handle this stream object.
My question is how do I handle the stream object to return the details of the transaction?
{ _readableState:
{ highWaterMark: 16384,
buffer: [],
length: 0,
pipes: null,
pipesCount: 0,
flowing: false,
ended: false,
endEmitted: false,
reading: false,
calledRead: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
objectMode: true,
defaultEncoding: 'utf8',
ranOut: false,
awaitDrain: 0,
readingMore: false,
decoder: null,
encoding: null },
readable: true,
domain: null,
_events: {},
_maxListeners: 10,
searchResponse: { stream: [Circular], success: true },
currentItem: 0,
currentOffset: 0,
bufferedResults: [] }
This returned the transaction data. I am going to keep looking into node streams, and am open to making this better, but as of now it at least returns the data.
stream.on("data", function (data) {
res.json(data);
});
This gives me an error though Error: Can't set headers after they are sent.
I think stream.pipe(res) should work.
The reason you were getting that "can't set headers after they are sent" error, is because the streams 'data' event was emitted multiple times, so you were trying to call res.json multiple times for the same request.
i want to make a request to a page using request module in node js, but this have a error 500 .
This is part of the json response
{
"readable": false,
"domain": null,
"_maxListeners": 10,
"httpVersion": "1.1",
"complete": true,
"_pendingIndex": 0,
"url": "",
"method": null,
"statusCode": 500,
"_consuming": true,
"_dumped": false,
"httpVersionMajor": 1,
"httpVersionMinor": 1,
"upgrade": false,
"body": "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>500 Not Found</title>\n</head><body>\n<h1>Not Found</h1>\n<p>The requested\n site HTTP/1.1 is not presently available on\n this web cluster.\n This may be a temporary issue, so please try again in a few\n moments.</p>\n\n</body></html>\n",
"_readableState": {
"highWaterMark": 16384,
"length": 0,
"pipes": null,
"pipesCount": 0,
"flowing": false,
"ended": true,
"endEmitted": true,
"reading": false,
"calledRead": true,
"sync": false,
"needReadable": true,
"emittedReadable": false,
"readableListening": false,
"objectMode": false,
"defaultEncoding": "utf8",
"ranOut": false,
"awaitDrain": 0,
"readingMore": false,
"decoder": null,
"encoding": null,
"buffer": []
},
This is the code that i use for the request
app.get('/', function(req, res){
var options = {
url: 'http://www.metallicabyrequest.com/'
}
request(options, function (error, response, html) {
/*if (!error && response.statusCode == 200) {
res.json(html);
}*/
if(error){
res.json(error);
}else{
res.json(response)
}
});
});
Is there any way to get prevent the error 500? I read that if you change the headers, is probably that the request works, but i don't know how to do ...
That site require 'Host' in request header:
var options = {
url: 'http://www.metallicabyrequest.com/',
headers: {
'Host': 'www.metallicabyrequest.com'
}
}
Sorry if I ask silly question, I am working with the example below regarding on nodejs long polling.
http://www.stoimen.com/blog/2010/12/02/diving-into-node-js-a-long-polling-example/
I understand most of them, but just one thing I am not quite able to understand although I have done thousands of searching.
fs.stat('filepath', function(err, stats) {
// if the file is changed
if (stats.mtime.getTime() > request.socket._idleStart.getTime()) {
// read it
fs.readFile('filepath', 'utf8', function(err, data) {
// return the contents
response.writeHead(200, {
'Content-Type' : 'text/plain',
'Access-Control-Allow-Origin' : '*'
});
// return response
response.write(data, 'utf8');
response.end();
// return
return false;
});
}
});
The part "request.socket._idleStart", what is the meaning of the parameter _idleStart? Actually, I try to print out the whole request object and got the following parameters.
_readableState:
{ highWaterMark: 16384,
buffer: [],
length: 0,
pipes: null,
pipesCount: 0,
flowing: false,
ended: false,
endEmitted: false,
reading: false,
calledRead: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
objectMode: false,
defaultEncoding: 'utf8',
ranOut: false,
awaitDrain: 0,
readingMore: false,
decoder: null,
encoding: null },
readable: true,
domain: null,
_events: {},
_maxListeners: 10,
socket:
....
...
...
I am wondering if there is any documentations describing these parameters, thanks for all for the help!
those parameters with _ underscore, are used for maintaining the state of socket, its not meant for working with them. there are functions that are more reliable than those.
from node.js documentation
readable._read
Note: This function should NOT be called directly. It should be
implemented by child classes, and called by the internal Readable
class methods only.
All Readable stream implementations must provide a _read method to
fetch data from the underlying resource.
This method is prefixed with an underscore because it is internal to
the class that defines it, and should not be called directly by user
programs. However, you are expected to override this method in your
own extension classes.