Creating LinkedNotebooks from API - javascript

I am working with the Evernote api trying to make linked notebooks from within our web application.
We are using evernote-sdk-js version of the API.
We are able to create the Shared Notebook from the API. Also we are able to create a Linked Notebook and receive a valid response. The problem is the linked notebook is not working. The notebook is returned in the ListLinkedNotebook api call but when I try to authorize it I receive an error.
Error { identifier: 'SharedNotebook.id', key: 'xxxx' }
From what I've read means the notebook is no longer shared by the owner. This is not true as the owner account still shows the pending request in the 'share' window.
Here is the logical flow:
1) User enters email address of the person they wish to share notebook with. A request is made to our server to create a ShareNotebook.
We create a Shared Notebook like this: (From Owner Account)
var notebook = new Evernote.SharedNotebook();
notebook.notebookGuid = guid;
notebook.email = email;
notebook.privilege = Evernote.SharedNotebookPrivilegeLevel.FULL_ACCESS;
var userclient = new Evernote.Client({token: token, sandbox: sandbox});
var notestore = userclient.getNoteStore();
notestore.createSharedNotebook(userclient.token, notebook, function(err, results){
callback(err, results);
});
With an example response like:
{
allowPreview:null
email:"...#gmail.com"
id:12345
notebookGuid:"d2fa3cbe-...."
notebookModifiable:false
privilege:4
recipientSettings:null
requireLogin:null
serviceCreated:1469079222000
serviceUpdated:1469079222000
shareKey:"xxxx-sxxx"
userId:999xxxxx
username:null
}
2) Since Evernote doesn't send an email from the API request we manually send the user a link to activate the Shared Notebook.
Code to create LinkedNotebook: (From Shared User Account)
var notebook = new Evernote.LinkedNotebook;
notebook.shareName = options.shareName;
notebook.username = options.username;
notebook.shardId = options.shardId;
notebook.shareKey = options.shareKey;
var userclient = new Evernote.Client({token: token, sandbox: sandbox});
var notestore = userclient.getNoteStore();
notestore.createLinkedNotebook(userclient.token, notebook, function(err, results){
callback(err, results);
});
And the example response:
{
shareName: 'Notebook Name',
username: '...',
shardId: 'sxxx',
shareKey: 'xxxx-sxxx',
uri: null,
guid: '4f8df3c2-...',
updateSequenceNum: 630,
noteStoreUrl: 'https://www.evernote.com/shard/sxxx/notestore',
webApiUrlPrefix: 'https://www.evernote.com/shard/sxxx/',
stack: null,
businessId: null
}
Has anyone had any luck creating a Linked Notebook from the API? It seems like the createLinkedNotebook API call is not working correctly. Thanks for your help.

tl;dr you can't do what you're trying to do right now, sorry.
So there are a couple pieces missing here, not something you can do much about.
The first piece is that the shared notebook must be "claimed" by a user (the recipient), during which it gets assigned to the user. Until it's claimed, a LinkedNotebook pointing to the SharedNotebook won't actually give you access to the underlying Notebook - authenticateToSharedNotebook will fail.
The second piece is the shareKey itself. We renamed the struct member you see as shareKey to globalId on the SharedNotebook and sharedNotebookGlobalId on the LinkedNotebook - we're currently in the process of updating our developer documentation, and then we'll update the sdks. To "claim" a sharedNotebook, the recipient needs to call authenticateToSharedNotebook with the shareKey, not the globalId. The catch is that we don't expose a method to generate the real shareKey, so there's no way for third-party clients to generate this and join the shared notebook. Once it is claimed, you can call authenticateToSharedNotebook with the globalId to get access to the notebook.
There's a shareNotebook method we added that send an email as part of the API call. That email contains a link for the user to click on which contains the shareKey and allows claiming a SharedNotebook, but that isn't currently in the SDKs or documented. We'll hopefully get it up with this round of doc updates.

Related

Stripe V3 Cardholder name on source card

I have MVC project and facing one issue with stripe flow. any help would be really appreciated.
Below is my flow.
I'm creating card token from UI using stripe js version v3. with this code, I'm able to retrieve both card and token perfectly from stripe.
stripe.createToken(card, { name: name }).then(function (result) {
if (result.error) {
// Inform the customer that there was an error.
} else {
// stripeTokenHandler will post the form to back end with token, here we are getting card with proper name,
// but we are just sending token to back end
stripeTokenHandler(result.token);
}
});
Then I use that token further to create a stripe source with C# back end code, for that I have used Stripe.net, Version=37.15.0.0m below is the code.
// creating a stripe customer, consider this is working fine
Stripe.Customer customer = GetCustomerService().Create(options, requestOptions);
// create the credit card source and attach it to the current StripeCustomerId
var options = new Stripe.SourceCreateOptions()
{
Type = "card",
Token = token,
};
var requestOptions = new Stripe.RequestOptions();
if (stripeAccountId.IsNotEmpty())
requestOptions.StripeAccount = stripeAccountId;
Stripe.Source source = null;
try
{
source = GetSourceService().Create(options, requestOptions);
}
catch (StripeException ex)
{
msgs.Add(ex.Message, MessageType.UserError, ex);
msgs.Add($"Payment source was not created", MessageType.UserError);
}
And this is how I'm retrieving stripe sources.
List<Stripe.Source>() sources = GetSourceService().List(customerId).ToList();
This all works fine, but the retrieved sources does not contains the name of card holder, which was there when we created and retrieved token. I tried to look at the documentation, but no luck.
I think it's issue in the flow, I might be missing some pieces but not sure what it is. Again, any help would be really appreciated. Thanks!
If you're building a net new project you should really look at using the newer Payment Methods / Payment Intents APIs instead of what you're doing here.
That said, you don't seem to be attaching the Token to the Customer, rather you seem to be creating a Source - which is different. If you do want to create the Source with a name, you'd want to provide that.

RabbitMQ amqp.node integration with nodejs express

The official RabbitMQ Javascript tutorials show usage of the amqp.node client library
amqp.connect('amqp://localhost', function(err, conn) {
conn.createChannel(function(err, ch) {
var q = 'hello';
ch.assertQueue(q, {durable: false});
// Note: on Node 6 Buffer.from(msg) should be used
ch.sendToQueue(q, new Buffer('Hello World!'));
console.log(" [x] Sent 'Hello World!'");
});
});
However, I find it's hard to reuse this code elsewhere. In particular, I don't know how to exports the channel object since it's in a callback. For example in my NodeJs/Express App:
app.post('/posts', (req, res) => {
-- Create a new Post
-- Publish a message saying that a new Post has been created
-- Another 'newsfeed' server consume that message and update the newsfeed table
// How do I reuse the channel 'ch' object from amqp.node here
});
Do you guys have any guidance on this one? Suggestion of other libraries is welcomed (Since I'm starting out, ease of use is what I considered the most important)
amqp.node is a low-level API set that does minimal translation from AMQP to Node.js. It's basically a driver that should be used from a more friendly API.
If you want a DIY solution, create an API that you can export from your module and manage the connection, channel and other objects from within that API file.
But I don't recommend doing it yourself. It's not easy to get things right.
I would suggest using a library like Rabbot (https://github.com/arobson/rabbot/) to handle this for you.
I've been using Rabbot for quite some time now, and I really like the way it works. It pushes the details of AMQP off to the side and lets me focus on the business value of my applications and the messaging patterns that I need, to build featurs.
As explained in the comments, you could use the module.exports to expose the newly created channel. Of course this will be overridden each time you create a new channel, unless you want to keep an array of channels or some other data structure.
Assuming this is in a script called channelCreator.js:
amqp.connect('amqp://localhost', function(err, conn) {
conn.createChannel(function(err, ch) {
var q = 'hello';
ch.assertQueue(q, {durable: false});
//this is where you can export the channel object
module.exports.channel = ch;
//moved the sending-code to some 'external script'
});
});
In the script where you may want to use the "exported" channel:
var channelCreator = require("<path>/channelCreator.js");
//this is where you can access the channel object:
if(channelCreator.channel){
channelCreator.channel.sendToQueue('QueueName', new Buffer('This is Some Message.'));
console.log(" [x] Sent 'Message'");
}
Hope this helps.

firebase javascript injection

I want ask something about firebase security. How to handle following situations?
User is creating account with createUserWithEmailAndPassword() function, then i save his username,email,created_at...to realtime db. But what if data are not saved correctly. His account is created and he is logged in automatically but data is not stored.
I have some registration logic... for example unique usernames... so before creating acc i check if this username exist in realtime db. But he still can call createUserWithEmailandPassword() from js console and account is created.
For situation one:
According to the firebase docs (https://www.firebase.com/docs/web/api/firebase/createuser.html), creating a user does not automatically authenticate them. An additional call to authWithPassword() is required first. In order to ensure that a user isn't authenticated without valid data, you could run a check to the server to make sure the data is saved correctly before authenticating.
Edit: Nevermind that; looks like firebase does auto-auth now - take a look at what I wrote below.
Now a concern with this approach would be if your app allowed people to authenticate with an OAuth provider like gmail, then there is no function for creating the user before authenticating them. What you may need to do is pull the user data from the firebase, determine if it's valid, and if its not valid show a popup or redirect that lets the user fix any invalid data.
For situation two:
If you wanted to make sure that in the case of them calling createUserWithEmailAndPassword() from the console a new user is not created, you could try something like this with promises;
var createUserWithEmailAndPassword = function(username, password) {
var promise = isNewUserValid(username, password);
promise.then(function() {
// Code for creating new user goes here
});
}
In this way, you never expose the actual code that makes a new user because it exists within an anonymous function.
I don't think that this could solve the problem entirely though because firebases API would let anyone create an account using something
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.createUser({
email: "bobtony#firebase.com",
password: "correcthorsebatterystaple"
}
(Taken from https://www.firebase.com/docs/web/api/firebase/createuser.html)
If you wanted to make sure that server side you can't ever create a user with the same user name, you'd need to look into firebases's rules, specifically .validate
Using it, you could make sure that the username doesn't already exist in order to validate the operation of creating a username for an account.
Here's the firebase doc on rules: https://www.firebase.com/docs/security/quickstart.html
And this is another question on stack overflow that is quite similar to yours. Enforcing unique usernames with Firebase simplelogin Marein's answer is a good starting point for implementing the server side validation.
First save the user credentials in the realtime database before you create the user:
var rootRef = firebase.database().ref('child');
var newUser = {
[name]: username,
[email]: useremail,
[joined]: date
};
rootRef.update(newUser);
After adding the Usersinfo into the realtime database create a new user:
firebase.auth().createUserWithEmailAndPassword(useremail, userpassword).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
When an error occured while inserting the data in the realtime database, it will skip the createuser function.
This works fine for me, hope this helps!

Parse iOS SDK: Understanding Cloud Code

Scenario = I am slowly but surely wrapping my head around what is going on with Parse's cloud code features. I just need some help from those who would like to answer some short, relatively simple questions about what is going on in some sample cloud code functions.
The code I will use in this example is below
1) cloud code
Parse.Cloud.define('editUser', function(request, response) {
var userId = request.params.userId,
newColText = request.params.newColText;
var User = Parse.Object.extend('_User'),
user = new User({ objectId: userId });
user.set('new_col', newColText);
Parse.Cloud.useMasterKey();
user.save().then(function(user) {
response.success(user);
}, function(error) {
response.error(error)
});
});
2) called from iOS
[PFCloud callFunction:#"editUser" withParameters:#{
#"userId": #"someuseridhere",
#"newColText": #"new text!"
}];
This code was taken from here
Question 1 =
(request, response)
I am confused by what this is. Is this like typecasting in iOS where I am saying (in the iOS call) I want to pass an NSString into this function ("userId") and inside the cloud code function I'm going to call it "request"? Is that what's going on here?
Question 2 =
Parse.Object.extend('_User')
Is this grabbing the "User" class from the Parse database so that a "PFObject" of sorts can update it by creating a new "user" in the line below it?
Is this like a...
PFObject *userObject = [PFObject objectWithClassName:#"User"]?
Question 3 =
user.set('new_col', newColText)
This obviously 'sets' the values to be saved to the PFUser (~I think). I know that the "newColText" variable is the text that is to be set - but what is 'new_col'? Only thing I can think of is that this sets the name of a new column in the database of whatever type is being passed through the "request"?
Is this like a...
[[PFUser currentUser] setObject: forKey:]
Question 4 =
Parse.Cloud.useMasterKey()
Without getting too technical, is this basically all I have to type before I can edit a "User" object from another User?
Question 5 =
user.save().then(function(user) {
response.success(user);
}
Is this like a...
[user saveInBackgroundWithBlock:]?
and if so, is
function(error) {
response.error(error)
just setting what happens if there is an error in the saveInBackgroundWithBlock?
Please keep in mind, I know iOS - not JavaScript. So try to be as descriptive as possible to someone who understands the Apple realm.
Here's my take on your questions:
The request parameter is for you to access everything that is part of the request/call to your cloud function, it includes the parameters passed (request.params), the User that is authenticated on the client (request.user) and some other things you can learn about in the documentation. The response is for you to send information back to the calling code, you generally call response.success() or response.error() with an optional string/object/etc that gets included in the response, again documentation here.
That's a way of creating an instance of a User, which because it is a special internal class is named _User instead, same with _Role and _Installation. It is creating an instance of the user with an ID, not creating a new one (which wouldn't have an ID until saved). When you create an object this way you can "patch" it by just changing the properties you want updated.
Again, look at the documentation or an example, the first parameter is the column name (it will be created if it doesn't exist), the second value is what you want that column set to.
You have to do Parse.Cloud.useMasterKey() when you need to do something that the user logged into the client doesn't have permission to do. It means "ignore all security, I know what I'm doing".
You're seeing a promise chain, each step in the chain allows you to pass in a "success" handler and an optional "error" handler. There is some great documentation. It is super handy when you want to do a couple of things in order, e.g.
Sample code:
var post = new Parse.Object('Post');
var comment = new Parse.Object('Comment');
// assume we set a bunch of properties on the post and comment here
post.save().then(function() {
// we know the post is saved, so now we can reference it from our comment
comment.set('post', post);
// return the comment save promise, so we can keep chaining
return comment.save();
}).then(function() {
// success!
response.success();
}, function(error) {
// uh oh!
// this catches errors anywhere in the chain
response.error(error);
});
I'm pretty much at the same place as you are, but here are my thoughts:
No, these are the parameters received by the function. When something calls the editUser cloud function, you'll have those two objects to use: request & response. The request is basically what the iOS device sent to the server, and response is what the server will send to the iOS device.
Not quite that. It's like creating a subclass of _User.
Think of Parse objects types as a database table and it's instances as rows. The set will set (derp) the value of 'newColText' to the attribute/column 'new_col'.
Not sure, never used that function as I don't handle User objects. But might be that.
Pretty much that. But it's more sort of like (pseudo-code, mixing JS with Obj-C):
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error){
if(error){
response.error(error); // mark the function as failed and return the error object to the iOS device
}
else{
response.success(user); // mark the function call as successful and return the user object to the iOS device
}
}];

javascript - make facebook page post

I am not using the Javascript SDK because that is client-side whereas I'm making a server-side call.
I want to make a page post so that I can make an ad creative with it. I can do the call perfectly fine in the Graph API Explorer tool, but I cannot make the same call (with the same long-lived access tokens that continue to work in the Graph Explorer) from Javascript. Here is my code:
tok = <valid and never expiring user token>;
var pg_tok = <valid and never expiring page token>;
var act_id = <account_id>;
var pg_id = <page_id>;
var call_to_action = 'INSTALL_MOBILE_APP';
var fb_app_url = 'https://itunes.apple.com/us/app/id284882215';
var msg = 'Test creative, ya see';
var pic_url = 'https://s3.amazonaws.com/<path_to_my_image>';
var ROOT = 'https://graph.facebook.com/';
var pagepost_endpoint = ROOT+pg_id+'/feed';
console.log(pagepost_endpoint);
var pagepost_params = {
access_token: pg_tok,
call_to_action: {
type: call_to_action,
value: {link: fb_app_url}
},
message: msg,
picture: pic_url,
published: false
};
console.log(pagepost_params);
var pagepost_res = HTTP.post(pagepost_endpoint, {params: pagepost_params});
console.log(pagepost_res);
I have played around a bunch with params vs. data for where pagepost_params goes in the HTTP.post that is giving the error (that is Meteor's HTTP btw).
-Putting everything in params gives the error: {"error":{"type":"Exception","message":"No Call To Action Type was parseable. Please refer to the call to action api documentation","code":1373054,"is_transient":false}}.
-Putting everything in data gives the error: {"error":{"message":"(#200) This API call requires a valid app_id.","type":"OAuthException","code":200}}.
-Putting access_token in params and everything else in data gives the error: {"error":{"message":"Invalid parameter","type":"FacebookApiException","code":100,"error_subcode":1349125}}.
One more clue for everyone, if I change the HTTP.post to HTTP.get, and just put access_token in params and include no other parameters (in params or in data), the call succeeds and I see past posts I have made on this page through the Graph Explorer (only the ones with published: true, though), so the access token and endpoint do work, just something is faulty about POST-ing instead of GET-ing and the specific parameters I'm using.
Have you tried posting to /photos instead of /feed? The error subcode is the same as mentioned here Posting to facebook wall using graph api
Hope this helps
Turned out to be an issue with Meteor's HTTP. It does not handle nested JSON very well, and we're going to submit a pull request for that. But for those seeing this, the important thing to take away is that the call_to_action may not be a valid JSON object, and even if it is, it may not be being stringified/parsed as expected. My fix was using request.post instead of HTTP.post. (then instead of params or data, you use form. look up node's request https://github.com/mikeal/request)

Categories