EverNoteからAPIが提供されているのを最近知りまして早速使ってみました。
(実際は大分前から提供されていたみたいですねw)
まずは準備。
EverNote APIは利用する際に、まず申請が必要となります。
以下のページから申請します。
質問の内容のところに、どんな目的でAPIを使いたいか記述して送信します。
早かったら、1営業日くらいでキーとパスワードが届きます。
APIキーが届いたら、https://sandbox.evernote.com/ で新たにアカウントを
作成します。正規のEverNoteの方で利用しているアカウント名と同じ名前でも
作成できます。
後は、http://www.evernote.com/about/developer/api/ からAPI本体をダウンロードします。
APIの中には、各言語用のサンプルとライブラリが入っています。
API自体は、Apache Thriftを用いて作成されています。
Javaとかの場合は、最初からjarファイルとかが用意されているのですが
C#の場合は、dllを自分でコンパイルしないといけません・・・。
APIのフォルダの中の
src/csharp/Thrift/
の中にVisual Studioソリューションファイルが存在します。
これでさっさとコンパイルしようと思ったら、開けませんでした・・・・。
「有効なソリューションファイルではありません」ってエラーが出ます。
しかたないので、自分でVSソリューションを作って中のソースを
放り込んでコンパイル。これでやっとDLLが手に入りました。
(いくつか警告がでるのでちょこちょこ修正)
サンプルを見ながら、テスト処理を記述。
C#のサンプルはMonoで作成しているっぽいです。
C#のサンプルソースのヘッダ部分にもgmcsでコンパイルする例が記述されてあります。
で、やっと使い方ですが利用するにはあまり難しくないです。
以下、要点部分を貼り付けていきます。
まずは、バージョンチェック部分。
利用しているAPIのバージョンをチェックします。
var userName = "xxxxx"; var password = "xxxxx"; var consumerKey = "xxxxx"; var consumerSecret = "xxxxx"; var defaultNotebookName = "xxxxx"; var host = @"https://sandbox.evernote.com"; var userStoreUrl = string.Format("{0}/edam/user", host); TTransport userStoreTransport = new THttpClient(userStoreUrl); TProtocol userStoreProtocol = new TBinaryProtocol(userStoreTransport); UserStore.Client userStore = new UserStore.Client(userStoreProtocol); bool versionOK = userStore.checkVersion( "EverNote Api Version Check", Evernote.EDAM.UserStore.Constants.EDAM_VERSION_MAJOR, Evernote.EDAM.UserStore.Constants.EDAM_VERSION_MINOR ); Assert.IsTrue(versionOK);
API全体でよくあるパターンとして
Transportを取得して、次にProtocolを取得、最後に目的の情報を持つxxStoreを取得
するという流れになっています。
ノートの場合は、最終的にNoteStoreを取得してノード情報を処理します。
次に、接続(認証)します。
認証が失敗する場合は例外が発生します。
AuthenticationResult authResult = null; try { authResult = userStore.authenticate(userName, password, consumerKey, consumerSecret); } catch (EDAMUserException ex) { Console.WriteLine("{0}, {1}", ex.ErrorCode, ex.Parameter); Assert.Fail(); }
認証が成功したら、後は必要な情報を集めて
ノートを操ります。まずはユーザ情報を取得。
User user = authResult.User;
Assert.AreEqual<string>(userName, user.Username);
String authToken = authResult.AuthenticationToken;
上記で取得している認証トークン(AuthenticationToken)がすごく重要です。
何かの情報を取得する際に、これが必要となります。
いよいよノートの取得.
String noteStoreUrl = string.Format("{0}/edam/note/{1}", host, user.ShardId); TTransport noteStoreTransport = new THttpClient(noteStoreUrl); TProtocol noteStoreProtocol = new TBinaryProtocol(noteStoreTransport); NoteStore.Client noteStore = new NoteStore.Client(noteStoreProtocol); List<Notebook> notebooks = noteStore.listNotebooks(authToken);
user.ShardIdは、user.SharedIdじゃないのかと思いつつ・・・。
EverNoteには、必ず一つはノートブックが存在します。(Default Notebook)
何も設定していない場合でも、notebooksのカウントは1となります。
実際にノートを登録してみます。
サンプル丸ぱくりなので、画像ファイル付きでノートを新規登録します。
byte[] image = (byte[]) new ImageConverter().ConvertTo(Properties.Resources.enlogo, typeof(byte[])); byte[] hash = new MD5CryptoServiceProvider().ComputeHash(image); string hashHex = BitConverter.ToString(hash).Replace("-", "").ToLower(); Data data = new Data(); data.Size = image.Length; data.BodyHash = hash; data.Body = image; Resource resource = new Resource(); resource.Mime = "image/png"; resource.Data = data; Note note = new Note(); note.NotebookGuid = defaultNotebook.Guid; note.Title = "Test note from EverNoteApiTest"; note.Content = GetContent(hashHex); note.Created = (long) (DateTime.Now - new DateTime(1970, 1, 1)).TotalMilliseconds; note.Updated = note.Created; note.Active = true; note.Resources = new List<Resource>(); note.Resources.Add(resource); Note createdNote = noteStore.createNote(authToken, note); private static string GetContent(string hashHex) { return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml.dtd\">" + "<en-note>Here's the Evernote logo:<br/>" + "<en-media type=\"image/png\" hash=\"" + hashHex + "\"/>" + "</en-note>"; }
流れとしては、
- 添付ファイルがある場合
- Noteインスタンスを作成
- 各属性を設定
- 本文はXML形式で設定する。
- 添付ファイルは、Resourcesプロパティにリストで設定する。
- 最後にnoteStore.createNoteで登録
となります。
次は更新。
Note n = noteStore.getNote(authToken, createdNote.Guid, true, true, true, true); n.Title = "Update From EverNoteApiTest"; note.Updated = (long)(DateTime.Now - new DateTime(1970, 1, 1)).TotalMilliseconds; noteStore.updateNote(authToken, n);
同じような感じです。ノートを取得して属性を変更。
ノートを取得する際には、トークンとノートのGUIDが必要になります。
最後にupdateNoteで更新します。
次に削除。
noteStore.deleteNote(authToken, createdNote.Guid);
削除されたノートは、Activeプロパティがfalseと設定されます。
またこの場合の削除は、物理削除ではなくてEverNote上の「ごみ箱」に移動されます。
で、最後に検索です。
検索は、NoteFilterを作成して検索します。
NoteFilter filter = new NoteFilter(); filter.Words = "EverNoteApiTest"; filter.NotebookGuid = defaultNotebook.Guid; filter.Inactive = false; NoteCollectionCounts findNoteCount = noteStore.findNoteCounts(authToken, filter, false); NoteList findNotes = noteStore.findNotes(authToken, filter, 0, 1000);
findNoteCountsで、合致したノートの数。
findNotesで、合致したノートを取得できます。
でも、私の環境で試してみたところ
NoteFilterのInactiveに明示的にfalse (削除されたノートを検索対象としない)と設定しているのに
検索すると削除されたノートまで含まれてきます。これバグなのかなぁ・・・使い方がわるいのか。
知っている方いらっしゃったら教えてください。 m(_ _)m
(とりあえず問い合わせてみます。)
最後に上記のソースの全体版です。
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Security.Cryptography; using Evernote.EDAM.Error; using Evernote.EDAM.NoteStore; using Evernote.EDAM.Type; using Evernote.EDAM.UserStore; using Microsoft.VisualStudio.TestTools.UnitTesting; using Thrift.Protocol; using Thrift.Transport; namespace EverNoteApiTest { [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { var userName = "xxxxx"; var password = "xxxxx"; var consumerKey = "xxxxx"; var consumerSecret = "xxxxx"; var defaultNotebookName = "xxxxx"; var host = @"https://sandbox.evernote.com"; var userStoreUrl = string.Format("{0}/edam/user", host); TTransport userStoreTransport = new THttpClient(userStoreUrl); TProtocol userStoreProtocol = new TBinaryProtocol(userStoreTransport); UserStore.Client userStore = new UserStore.Client(userStoreProtocol); bool versionOK = userStore.checkVersion( "EverNote Api Version Check", Evernote.EDAM.UserStore.Constants.EDAM_VERSION_MAJOR, Evernote.EDAM.UserStore.Constants.EDAM_VERSION_MINOR ); Assert.IsTrue(versionOK); AuthenticationResult authResult = null; try { authResult = userStore.authenticate(userName, password, consumerKey, consumerSecret); } catch (EDAMUserException ex) { Console.WriteLine("{0}, {1}", ex.ErrorCode, ex.Parameter); Assert.Fail(); } User user = authResult.User; Assert.AreEqual<string>(userName, user.Username); String authToken = authResult.AuthenticationToken; String noteStoreUrl = string.Format("{0}/edam/note/{1}", host, user.ShardId); TTransport noteStoreTransport = new THttpClient(noteStoreUrl); TProtocol noteStoreProtocol = new TBinaryProtocol(noteStoreTransport); NoteStore.Client noteStore = new NoteStore.Client(noteStoreProtocol); List<Notebook> notebooks = noteStore.listNotebooks(authToken); Assert.AreEqual<int>(1, notebooks.Count); Notebook defaultNotebook = null; foreach (Notebook notebook in notebooks) { if (notebook.DefaultNotebook) { defaultNotebook = notebook; } } Assert.IsNotNull(defaultNotebook); Assert.AreEqual<string>(defaultNotebookName, defaultNotebook.Name); byte[] image = (byte[]) new ImageConverter().ConvertTo(Properties.Resources.enlogo, typeof(byte[])); byte[] hash = new MD5CryptoServiceProvider().ComputeHash(image); string hashHex = BitConverter.ToString(hash).Replace("-", "").ToLower(); Data data = new Data(); data.Size = image.Length; data.BodyHash = hash; data.Body = image; Resource resource = new Resource(); resource.Mime = "image/png"; resource.Data = data; Note note = new Note(); note.NotebookGuid = defaultNotebook.Guid; note.Title = "Test note from EverNoteApiTest"; note.Content = GetContent(hashHex); note.Created = (long) (DateTime.Now - new DateTime(1970, 1, 1)).TotalMilliseconds; note.Updated = note.Created; note.Active = true; note.Resources = new List<Resource>(); note.Resources.Add(resource); Note createdNote = noteStore.createNote(authToken, note); Assert.AreEqual<string>("Test note from EverNoteApiTest", createdNote.Title); Assert.IsTrue(createdNote.Active); var bodyHash = createdNote.Resources.First().Data.BodyHash; for (int i = 0; i < bodyHash.Length; i++) { Assert.AreEqual<byte>(hash[i], bodyHash[i]); } Note n = noteStore.getNote(authToken, createdNote.Guid, true, true, true, true); n.Title = "Update From EverNoteApiTest"; note.Updated = (long)(DateTime.Now - new DateTime(1970, 1, 1)).TotalMilliseconds; noteStore.updateNote(authToken, n); n = noteStore.getNote(authToken, createdNote.Guid, true, true, true, true); Assert.AreEqual<string>("Update From EverNoteApiTest", n.Title); //NoteFilter filter = new NoteFilter(); //filter.Words = "EverNoteApiTest"; //filter.NotebookGuid = defaultNotebook.Guid; //filter.Inactive = false; //NoteCollectionCounts findNoteCount = noteStore.findNoteCounts(authToken, filter, false); //Assert.AreEqual<int>(1, findNoteCount.NotebookCounts[defaultNotebook.Guid]); //NoteList findNotes = noteStore.findNotes(authToken, filter, 0, 1000); //Assert.AreEqual<int>(1, findNotes.Notes.Count); //Assert.AreEqual<string>(n.Guid, findNotes.Notes.First().Guid); noteStore.deleteNote(authToken, createdNote.Guid); n = noteStore.getNote(authToken, createdNote.Guid, true, true, true, true); Assert.IsFalse(n.Active); } private static string GetContent(string hashHex) { return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml.dtd\">" + "<en-note>Here's the Evernote logo:<br/>" + "<en-media type=\"image/png\" hash=\"" + hashHex + "\"/>" + "</en-note>"; } } }
こんな感じです。
思ってたよりも使いやすいAPIでした。まだまだ試していないAPIはいっぱいあるので
ちょこちょこ勉強してみます。(データの同期とか)
折角環境構築したので、設定したVisualStudioプロジェクトと
ビルドしたDLLを以下からダウンロードできるようにしています。
てっとり早く試してみたい方はどうぞ。
(プロジェクトはVisualStudio 2010形式ですが、ビルドターゲットは3.5にしてあります。)
- VisualStudio 2010プロジェクトとDLLファイル
================================
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ