More Related Content Similar to Data api workshop at Co-Edo Similar to Data api workshop at Co-Edo (20) More from Yuji Takayama (17) Data api workshop at Co-Edo1. Data API 勉強会 Vol.1
2014.6.11 @ Co-Edo
YUJI Takayama@Six Apart
2. About Me
• 名前: 高山 裕司
• 会社: シックス・アパート
• 仕事: Movable Type 全般
• プロダクト・マネージャ
• リード・エンジニア
github.com/yuji
@yuji
Yuji Takayama
5. • Movable Type 6 から搭載
• Movable Type に保存されているデー
タを操る
• REST API
• MT のユーザー管理、認証機構を利用
10. Data API で出来ること
Create Read Update Delete
Entry ○ ○ ○ ○
Comment ○ ○ ○ ○
Trackback ○ ○ ○
User ○ ○
Site(Blog,Website) ○
Category ○
Site Statistics ○
Asset ○
13. 呼び出し方で処理が変わる
POST GET PUT DELETE
Entry ○ ○ ○ ○
Comment ○ ○ ○ ○
Trackback ○ ○ ○
User ○ ○
Site(Blog,Website) ○
Category ○
Site Statistics ○
Asset ○
同じエンドポイントへのリクエストで変わる
14. 結果は JSON 形式
{!
"url" : "http://localhost/blog/20140609-1/",!
"archiveUrl" : "http://localhost/blog/20140609-1/",!
"name" : "First Website",!
"class" : "website",!
"id" : "1",!
"description" : null!
}
18. ユーザー認証
curl -i -d clientId=test -d username=takayama -d password=password
http://localhost/cgi-bin/mt/mt-data-api.cgi/v1/authentication -X POST
リクエスト
応答
HTTP/1.1 200 OK
Date: Mon, 09 Jun 2014 07:01:38 GMT
Server: Apache/2.2.26 (Unix) mod_ssl/2.2.26 OpenSSL/1.0.1e DAV/2 PHP/5.3.27
X-content-type: nosniff
Cache-control: no-cache
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
X-Pad: avoid browser bug
!
{"sessionId":"vjqLFlFxEki5ElAHy9DyUe6k3jAuevXJyGdjXk8j","accessToken":"h8YOleAJo
aBkNCJl3kmOgmlsocNo0J5LiwZljAV9","expiresIn":3600,"remember":false}
19. 記事の投稿
curl -i http://localhost/cgi-bin/mt/mt-data-api.cgi/v1/sites/1/entries -X POST
-H "X-MT-Authorization: MTAuth accessToken=h8YOleAJoaBkNCJl3kmOgmlsocNo0J5LiwZljAV9”
-d entry="{"status" : "Publish", "title" : "This is test post", "body" :
"Yeah!" }"
リクエスト
応答
HTTP/1.1 200 OK
Date: Mon, 09 Jun 2014 07:27:00 GMT
Server: Apache/2.2.26 (Unix) mod_ssl/2.2.26 OpenSSL/1.0.1e DAV/2 PHP/5.3.27
X-content-type: nosniff
Cache-control: no-cache
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
X-Pad: avoid browser bug
!
{"excerpt":"Yeah!...","date":"2014-06-09T16:27:01u002b09:00","status":"Publish","updatable":true,"author":
{"userpicUrl":null,"id":"1","displayName":"Yuji Takayama"},"comments":[],"allowComments":true,"permalink":"http://localhost/blog/
20140609-1/2014/06/this-is-test-post.html","keywords":"","body":"Yeah!","trackbacks":[],"id":
4,"allowTrackbacks":false,"modifiedDate":"2014-06-09T16:27:01u002b09:00","trackbackCount":0,"blog":{"id":"1"},"categories":[],"tags":
[],"commentCount":0,"assets":[],"basename":"this_is_test_post","createdDate":"2014-06-09T16:27:01u002b09:00","class":"entry","title":"This
is test post","pingsSentUrl":[],"more":"","customFields":[]}% /Users/takayama% curl -
i http://localhost/cgi-bin/mt/mt-data-api.cgi/v1/sites/1/entries -X POST -H "X-MT-Authorization: MTAuth
accessToken=h8YOleAJoaBkNCJl3kmOgmlsocNo0J5LiwZljAV9" -d entry="{"status" : "Publish", "title" : "This is test post", "body" :
"Yeah!" }"
22. • REST API の呼び出しをラッピング
• シンプルな実装なので、他のライブラリ
と干渉しにくい
• 他の言語系も順次サポート予定
23. 使い方
var api = new MT.DataAPI({
baseUrl: "https://your-‐host/mt/mt-‐data-‐api.cgi",
clientId: "your-‐client-‐id"
});
!
api.listEntries(siteId, function(response) {
if (response.error) {
// Handle error
return;
}
!
for (var i = 0; i < response.items.length; i++) {
var entry = response.items[i];
// Render an entry
}
});
<script type=“text/javascript” src=“http://your-‐host/path/to/mt-‐static/
data-‐api/v1/js/mt-‐data-‐api.min.js”></script>
24. var api = new MT.DataAPI({
baseUrl: "https://your-‐host/mt/mt-‐data-‐api.cgi",
clientId: "your-‐client-‐id"
});
!
api.getToken(function(response) {
if (response.error) {
if (response.error.code === 401) {
// You have not been authenticated yet.
location.href = api.getAuthorizationUrl(location.href);
} else { /* Handle error */ }
} else {
// You have been authenticated.
api.listEntries(siteId, {status: 'Draft'}, function(response) {
if (response.error) { /* Handle error */ return; }
// Fetched a list of drafts.
for (var i = 0; i < response.items.length; i++) {
var entry = response.items[i];
// Render an entry
}
});
}
});
27. • Movable Type powered by
JavaScript
• 記事の投稿に特化した Chrome アプリ
• Data API + jQuery + Bootstrap3
• MIT でフォーク自由
29. 初期化
jQuery(document).ready( function() {
getSettings().then(
function(settings) {
if (!settings || !settings.apipath) {
return jQuery('#setting-‐panel-‐dialog').modal();
}
!
api = new MT.DataAPI({
baseUrl: settings.apipath,
clientId: "movabletype-‐writer"
});
!
doSignIn(settings.username, settings.password)
.then( loadSiteList );
.
.
.
30. 初期化
jQuery(document).ready( function() {
getSettings().then(
function(settings) {
if (!settings || !settings.apipath) {
return jQuery('#setting-‐panel-‐dialog').modal();
}
!
api = new MT.DataAPI({
baseUrl: settings.apipath,
clientId: "movabletype-‐writer"
});
!
doSignIn(settings.username, settings.password)
.then( loadSiteList );
.
.
.
ローカルストレージから、設定内容を読み込む。
設定されていなければ、設定用のダイアログを表示する
31. 初期化
jQuery(document).ready( function() {
getSettings().then(
function(settings) {
if (!settings || !settings.apipath) {
return jQuery('#setting-‐panel-‐dialog').modal();
}
!
api = new MT.DataAPI({
baseUrl: settings.apipath,
clientId: "movabletype-‐writer"
});
!
doSignIn(settings.username, settings.password)
.then( loadSiteList );
.
.
.Data API のインスタンスを生成する
baseUrl には Data API へのURL
clientId は任意の文字列
32. 初期化
jQuery(document).ready( function() {
getSettings().then(
function(settings) {
if (!settings || !settings.apipath) {
return jQuery('#setting-‐panel-‐dialog').modal();
}
!
api = new MT.DataAPI({
baseUrl: settings.apipath,
clientId: "movabletype-‐writer"
});
!
doSignIn(settings.username, settings.password)
.then( loadSiteList );
.
.
.
ユーザー認証を行う
その後、サイト一覧を更新する
33. サインイン
var doSignIn = function(user, passwd) {
var def = new jQuery.Deferred();
!
api.authenticate({ username: user, password: passwd },
function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 401 ) {
} else {
}
return def.reject(msg);
} else {
api.storeTokenData(response);
return def.resolve();
}
});
!
return def.promise();
};
34. サインイン
var doSignIn = function(user, passwd) {
var def = new jQuery.Deferred();
!
api.authenticate({ username: user, password: passwd },
function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 401 ) {
} else {
}
return def.reject(msg);
} else {
api.storeTokenData(response);
return def.resolve();
}
});
!
return def.promise();
};
認証用のエンドポイントを呼び出す。
認証エラーは、response.error の有無で判定
認証に成功したら、storeTokenData でセッションデータを保存
35. サイト一覧の取得
var loadSiteList = function() {
var def = new jQuery.Deferred();
!
api.listBlogsForUser('me', function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 403 ) {
} else {
}
return def.reject(msg);
}
!
var $blogListBox = jQuery('#form-‐blog-‐list');
response.items.forEach( function(x, i) {
$blogListBox.append($('<option>').html(x.name).val(x.id));
});
$blogListBox.removeAttr('disabled');
});
36. サイト一覧の取得
var loadSiteList = function() {
var def = new jQuery.Deferred();
!
api.listBlogsForUser('me', function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 403 ) {
} else {
}
return def.reject(msg);
}
!
var $blogListBox = jQuery('#form-‐blog-‐list');
response.items.forEach( function(x, i) {
$blogListBox.append($('<option>').html(x.name).val(x.id));
});
$blogListBox.removeAttr('disabled');
});
ユーザーが権限を持つサイトの一覧を取得する
エラー 404: サイトが見つからない
エラー: 403: 認証エラー
37. サイト一覧の取得
var loadSiteList = function() {
var def = new jQuery.Deferred();
!
api.listBlogsForUser('me', function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 403 ) {
} else {
}
return def.reject(msg);
}
!
var $blogListBox = jQuery('#form-‐blog-‐list');
response.items.forEach( function(x, i) {
$blogListBox.append($('<option>').html(x.name).val(x.id));
});
$blogListBox.removeAttr('disabled');
});
セレクトボックスに読み込んだサイトの一覧を追加
38. 記事の投稿
jQuery('#button-‐post').click( function() {
var entry = {};
entry['title'] = jQuery('#entry-‐title').val();
entry['body'] = jQuery('#entry-‐body').code();
entry['status'] = 'Publish';
var siteId = jQuery('#form-‐blog-‐list option:selected').val();
api.getToken(function(response) {
if (response.error) { }
api.createEntry(siteId, entry, function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 401 ) {
} else if (code === 403 ) {
} else {
}
}
}
}
}
39. 記事の投稿
jQuery('#button-‐post').click( function() {
var entry = {};
entry['title'] = jQuery('#entry-‐title').val();
entry['body'] = jQuery('#entry-‐body').code();
entry['status'] = 'Publish';
var siteId = jQuery('#form-‐blog-‐list option:selected').val();
api.getToken(function(response) {
if (response.error) { }
api.createEntry(siteId, entry, function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 401 ) {
} else if (code === 403 ) {
} else {
}
}
}
}
}
エントリーデータをハッシュで作成
40. 記事の投稿
jQuery('#button-‐post').click( function() {
var entry = {};
entry['title'] = jQuery('#entry-‐title').val();
entry['body'] = jQuery('#entry-‐body').code();
entry['status'] = 'Publish';
var siteId = jQuery('#form-‐blog-‐list option:selected').val();
api.getToken(function(response) {
if (response.error) { }
api.createEntry(siteId, entry, function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 401 ) {
} else if (code === 403 ) {
} else {
}
}
}
}
}
トークンの有効期限が過ぎている場合は、再取得が必要
41. 記事の投稿
jQuery('#button-‐post').click( function() {
var entry = {};
entry['title'] = jQuery('#entry-‐title').val();
entry['body'] = jQuery('#entry-‐body').code();;
entry['status'] = 'Publish';
var siteId = jQuery('#form-‐blog-‐list option:selected').val();
api.getToken(function(response) {
if (response.error) { }
api.createEntry(siteId, entry, function(response) {
if (response.error) {
var code = response.error.code;
var msg;
if (code === 404 ) {
} else if (code === 401 ) {
} else if (code === 403 ) {
} else {
}
}
}
}
}
エントリの作成をおこなう
エラー 404: 投稿先のサイトが見つからない
エラー 403: 権限がない
エラー 401: 認証エラー