21 April 2009

Evernote API или Что внутри слона?

Evernote corporate blog
В комментариях к нашим предыдущим записям в блоге на Хабре мы упомянули о том, что Evernote имеет собственный API. Сейчас я хочу рассказать о нем подробнее.


Когда мы только разрабатывали протокол синхронизации для Evernote, мы хотели добиться кроссплатформенности решения (веб-часть написана на Java, клиент для Windows и Windows Mobile — на C++, клиенты для Mac и iPhone на Objective C). Нужно было что-то, что позволит легко имплементировать этот протокол на разных клиентах и позволять расширять этот протокол по необходимости. Помимо этого, нам хотелось обеспечить передачу бинарных данных по данному протоколу, дабы уменьшить объем передаваемых данных. Выбор пал на Thrift, протокол, использовавшийся (и до сих пор использующийся) в Facebook, а ныне ставший опенсорсным проектом под крылом Apache Incubator. Разрабочики Evernote даже приложили свою руку к развитию проекта, исправляя ошибки и дорабатывая Java, Objective C и Perl-реализациии Thrift.

Что такое Thrift?

Thrift — это реализация RPC, позволяющая удаленно вызывать функции на сервере. Thrift состоит из трех основных вещей:
  1. бинарный протокол — способ упаковки структур данных;
  2. собственный декларативный язык описания объектов, их свойств и методов;
  3. генераторы кода для разных языков (Java, C, Ruby, Python, Perl, PHP и др.), которые на основе описанных объектов генерируют родной код, позволяющий получать объекты с сервера и вызывать их методы совершенно прозрачно.

Итак, Evernote API — это не что иное как реализация специфических для нас объектов (Пользователь, Блокнот, Заметка и т.д.) и их свойств, описанных на языке Thrift. Все существующие клиенты Evernote сами используют это API, так что здесь нет разделения на «свой-чужой». Все приложения — как наши, так и сторонние — равноправны и пользуются одними и теми же функциями.

Для чего нужен Evernote API?

Существует два основных способа исппользования API:

1) Написание клиентских приложений для Evernote. В этом случае клиентское приложение авторизуется в системе под именем и паролем конкретного пользователя и получает доступ ко всем объектам. Этот способ должен использоваться теми сервисами, которые подразумевают работу только с одним конкретным пользователем Evernote.

2) Написание модулей интеграции (плагинов) для работы сторонних сервисов с самой системой. В этом случае мы используем протокол OAuth, который дает возможность пользователям Evernote предоставлять ограниченный доступ внешним приложениям к своему аккаунту без необходимости раскрывать свой пароль. Этот способ должен использоваться теми сервисами, которые подразумевают работу с несколькими пользователями Evernote.

Идеи использования Evernote API

1) Плагин для популярного движка для блогов (типа WordPress), автоматически публикующий в блоге заметки из определенного блокнота в Evernote. Зачем? Представьте, что вы можете подготавливать посты для блога в офлайне, на любом клиенте Evernote. Там же, где и собирать информацию для своего блога. Это удобство ввода информации, предоставляемое Evernote и огромные возможности настройки внешнего вида и комментирования записей, предоставляемые движками блогов.

2) Специфические клиенты и клипперы. Например, приложение, распознающее с фотокамеры шрихкод, получающее по этому штрихкоду сведения в Интернете и формирующее заметку в Evernote. Зачем? Вы ходите по магазину, фотографируете ценники товаров (например, книг) на свой телефон, а дома в Evernote уже сравниваете понравившиеся товары, получаете по ним дополнительные сведения, цены из Интернет-магазинов. Сюда же можно отнести захват фото с веб-камер или интеграция со сканерами через TWAIN.

3) Связь с системами планирования дел. Представьте, что вы с любого клиента Evernote вводите заметки вида «завтра в одиннадцать деловая встреча в офисе», а задача с напоминанием попадает в сервис, подобный Google Calendar, Remember The Milk, который впоследствии оповестит вас о запланированном деле по SMS или электронной почте.

4) По аналогии с интеграцией Evernote и Твиттера, можно придумать сервисы импорта из ICQ, XMPP (Jabber), c помощью SMS.

Я думаю, что вы сами придумаете и другие интересные способы интеграции с Evernote, о которых мы даже не догадываемся. Уже насчитывается 900 000 пользователей Evernote по всему миру, и взаимодействие с Evernote — это хороший шанс повысить ценность вашего продукта. Про некоторые из реализованных примеров интеграции можно прочитать в нашем англоязычном блоге.

С чего начать?

Попробовать наше API очень просто — скачайте нужный SDK на этой странице, в котором есть примеры использования и интерфейсные библиотеки и отправьте там же запрос на получение API-ключа, вкратце написав о себе (название проекта, для чего API нужен и как будет использоваться). После получения ключа можно смело экспериментировать с нашим API на специальном тестовом сервере-«песочнице», не боясь сделать что-то не то. Обратите внимание, что для работы в «песочнице» там надо завести отдельный аккаунт пользователя Evernote, ибо на этом сервере используется своя база пользователей, не пересекающаяся с основной.

Не забывайте посещать наш форум для разработчиков и задавать там любые вопросы по API. Или спрашивайте здесь — постараемся помочь.

Отдельно стоит отметить, что лицензия на Evernote API не ограничивает разработчиков в способе распространения приложений. Это могут быть бесплатные и коммерческие разработки, open-source проекты и проекты с закрытыми исходниками. Тут все остается на ваше усмотрение.

Пример кода

Напоследок приведу пример кода на Python, авторизующего пользователя по его имени и паролю, читающего из STDIN тело заметки и создающего заметку на сервере:
  1. #!/usr/bin/python
  2. # coding=utf-8
  3. # импортируем модули (поставляются с Evernote API)
  4. import time
  5. import thrift.transport.THttpClient as THttpClient
  6. import thrift.protocol.TBinaryProtocol as TBinaryProtocol
  7. import evernote.edam.userstore.UserStore as UserStore
  8. import evernote.edam.userstore.constants as UserStoreConstants
  9. import evernote.edam.notestore.NoteStore as NoteStore
  10. import evernote.edam.type.ttypes as Types
  11. # настраиваем параметры сервера
  12. edamHost = "sandbox.evernote.com" # сервер-"песочница", на котором можно эксперименитровать
  13. edamPort = 443
  14. # настраиваем параметры авторизации
  15. consumerKey = "your-api-key-here!" # ключ, полученный от Evernote
  16. consumerSecret = "your-api-secret-here!" # секретная часть ключа, полученного от Evernote
  17. username = "your-username" # имя пользователя в Evernote (на сервере sandbox.evernote.com)
  18. password = "your-password" # пароль пользователя в Evernote
  19. # создаем подключение по HTTP к UserStore (хранилищу объектов типа Пользователь)
  20. userStoreHttpClient = THttpClient.THttpClient(edamHost, edamPort, "/edam/user")
  21. userStoreProtocol = TBinaryProtocol.TBinaryProtocol(userStoreHttpClient)
  22. userStore = UserStore.Client(userStoreProtocol)
  23. # убеждаемся, что версия нашего клиента не устарела
  24. versionOK = userStore.checkVersion("Python EDAMTest",
  25. UserStoreConstants.EDAM_VERSION_MAJOR,
  26. UserStoreConstants.EDAM_VERSION_MINOR)
  27. print "Is my EDAM protocol version up to date? ", str(versionOK)
  28. if not versionOK:
  29. exit(1)
  30. # авторизуем пользователя по имени и паролю
  31. authResult = userStore.authenticate(username, password, consumerKey, consumerSecret)
  32. # получаем объект User
  33. user = authResult.user
  34. # получаем токен авторизации для последующих запросов к серверу
  35. authToken = authResult.authenticationToken
  36. print "Authentication was successful for ", user.username
  37. print "Authentication token = ", authToken
  38. # создаем подключение по HTTP к NoteStore (хранилищу объектов типа Заметка)
  39. noteStoreUri = "/edam/note/" + user.shardId
  40. noteStoreHttpClient = THttpClient.THttpClient(edamHost, edamPort, noteStoreUri)
  41. noteStoreProtocol = TBinaryProtocol.TBinaryProtocol(noteStoreHttpClient)
  42. noteStore = NoteStore.Client(noteStoreProtocol)
  43. # получаем список блокнотов
  44. notebooks = noteStore.listNotebooks(authToken)
  45. # выводим перечень блокнотов в STDOUT и попутно выясняем,
  46. # какой из блокнотов является блокнотом по умолчанию
  47. print "Found ", len(notebooks), " notebooks:"
  48. for notebook in notebooks:
  49. print " * ", notebook.name
  50. if notebook.defaultNotebook:
  51. defaultNotebook = notebook
  52. # переходим к созданию заметки в блокноте по умолчанию
  53. print
  54. print "Creating a new note in default notebook: ", defaultNotebook.name
  55. print
  56. # создаем новый объект типа Заметка
  57. note = Types.Note()
  58. # указываем блокнот для данной заметки
  59. note.notebookGuid = defaultNotebook.guid
  60. # формируем заголовок
  61. note.title = raw_input("Note title? ").strip()
  62. # формируем тело заметки в формате ENML (по сути это XHTML с некоторыми особенностями)
  63. note.content = '<?xml version="1.0" encoding="UTF-8"?>'
  64. note.content += '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml.dtd">'
  65. note.content += '<en-note>'
  66. note.content += raw_input("Well-formed XHTML note content? ").strip() # вводим XTML из STDIN
  67. note.content += '</en-note>'
  68. # задаем текущее время как время создания и модификации заметки
  69. note.created = int(time.time() * 1000) # время указывается в миллисекундах
  70. note.updated = note.created
  71. # собственно, создаем заметку на сервере
  72. createdNote = noteStore.createNote(authToken, note)
  73. print "Created note: ", str(createdNote)
Tags:evernoteapithriftoauthпример кода
Hubs: Evernote corporate blog
+31
13.2k 57
Comments 27