More than 3 years have passed since last update.
DirectCloud-BOXのAPIを呼び出すとできそうな、ファイルベースRPC
macOS標準搭載のAppleScriptにおいてもWeb APIの利用はもはや一般的になっています。日常的に「じゃあこの処理はWeb APIでやろう」という話はザラです。
👁 スクリーンショット 2021-08-24 13.24.16.jpeg
写真.appで選択中の写真をMicrosoft Cognitive APIで画像認識してタグをつけるとか、タグが英語で書かれていて整理しにくいからGoogle APIを呼び出して日本語に翻訳してタグづけしようとか、Finder上で選択しているファイルをDropboxにアップロードして共有リンクを作成してクリップボードに設定するなど枚挙にいとまがありません。
Xcode上でアプリケーションを開発しているときにも、画面上に出す文言をWeb APIで数か国語ぶん翻訳して使うといったこともしています。そのまま使わず、きちんと検証は行っていますが、それでも見たことも聞いたこともない言語への翻訳に、こうした「道具」が使えることの意義は大きいです。
▲Mac App Storeユーティリティ部門で最高33位を獲得した「Uni Detector」もすべてAppleScriptで書いたアプリケーション
話を戻しますが……さまざまなローカルのアプリケーション、ローカルのデータ、Web APIのサービスをまぜて利用する(プライバシーへの配慮を行いつつ)というのが、2021年的なAppleScriptの利用の仕方になっています。
今回、Qiitaエンジニアフェスタのお知らせを見かけ、冒頭のようにREST APIなら割と国内外のさまざまなサービスを呼び出して処理を行っていたので、DirectCloud-BOXという初めてお目にかかるサービスへの扉を開いたのです。
まずは、ユーザー認証
どのWeb APIでもユーザー認証を行うことが前提です。ユーザー認証せずに使い始められるWeb APIは(ほとんど)ないので、これをクリアできなければ他の機能を試すことはほとんどできませんし、これができたからといって他のAPIの機能を使いこなせるというものでもありません。
ここでつまづくパターンが割と多いので、このあたりは重点的にサンプルコードが掲載されているといいですよね。本命の機能ではPOSTメソッドを使っていても、ユーザー認証機能のあたりはGETメソッドで実装されているWebサービスがほとんどです。
これは、サービスの設計がひとつのチュートリアルになっていて、「まずはご挨拶」ということで技術的な難易度の低いGETメソッドで呼んでみることができて「ああ、このサービスは動いているんだな」「自分の環境から確実に呼べて、応答してもらっているんだな」という信頼関係が、サービス運営側と呼び出し側との間で築かれるように感じます。
アクションゲームの最初のステージが、とても簡単な内容になっていて、その最初のステージをクリアすることでプレイヤーがゲーム全体の仕組みを理解できるようになっている、という設計に近い話だと思います。とくに、このあたりは任天堂さんのゲームにそうした姿勢が見られますよね。
API利用ドキュメントにcurlで呼び出すサンプルが掲載されていたりすると、まずは呼び出して確認ができます。curlコマンド実行サンプルは「命綱」のように感じています。
ユーザー認証であきらめて「次」へ
肝心のDirectCloud-BOXへのアクセスについては、ドキュメントを見てもさっぱりわかりません。
ユーザーサポートに問い合わせていろいろ聞いてみたものの、問い合わせないとわからない重要情報(なぜかドキュメントに書かれていない、エンドポイントURL)、仕様どおりに実装されていない各種機能(使い始めた時点ではメッセージの言語指定が無視されていた)、後出しジャンケンのように判明する技術仕様のかずかずに目を回してしまいました。
--ご注意:アカウント関連情報を入れてもエラーが返ってきますuseAppleScriptversion"2.7"usescriptingadditionsuseframework"Foundation"propertyNSString:areference tocurrent application's NSStringpropertyNSUTF8StringEncoding:areference tocurrent application's NSUTF8StringEncodingsetreqURLStrto"https://api.directcloud.jp/openapi/jauth/token"setaPostDatato{service:"service_account",service_key:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",code:"xxxxxxxx",|id|:"xxxxxxxx",|password|:"xxxxxxxxxxx"}setaUUIDto(current application's NSUUID's UUID's UUIDString())setaBodyDattorecToMultipartFormDat(aPostData,aUUID)ofmesetaRestocallRestPOSTAPIAndParseResults(reqURLStr,aBodyDat,aUUID)ofmereturnaRessetaRESCodeto(responseCodeofaRes)asintegerifaRESCodeis notequal to200thenreturnfalsesetaRESHeadertoresponseHeaderofaRessetaRESTrestojsonofaRes--POST methodのREST APIを呼ぶon callRestPOSTAPIAndParseResults(aURL,aPostData,aUUID)--set dataJson to convRecToJson(aPostData) of mesetaRequesttocurrent application's NSMutableURLRequest's requestWithURL:(current application's |NSURL|'s URLWithString:aURL)aRequest's setHTTPMethod:"POST"--aRequest's setCachePolicy:(current application's NSURLRequestReloadIgnoringLocalCacheData)aRequest's setHTTPShouldHandleCookies:falseaRequest's setTimeoutInterval:60setaBoundarytogenerateBundaryTop(aUUID)ofmesetcontentTypetoNSString's stringWithFormat_("multipart/form-data; boundary=%@",aBoundary)aRequest's setValue:contentTypeforHTTPHeaderField:"Content-Type"aRequest's setHTTPBody:aPostDatasetaRestocurrent application's NSURLConnection's sendSynchronousRequest:aRequestreturningResponse:(reference)|error|:(missing value)setresListtoaResaslistsetbRestocontentsof(firstitemofresList)setresStrtocurrent application's NSString's alloc()'s initWithData:bResencoding:(current application's NSUTF8StringEncoding)setjsonStringtocurrent application's NSString's stringWithString:resStrsetjsonDatatojsonString's dataUsingEncoding:(current application's NSUTF8StringEncoding)setaJsonDicttocurrent application's NSJSONSerialization's JSONObjectWithData:jsonDataoptions:0|error|:(missing value)--Get Response Code & HeadersetdRestocontentsofseconditemofresListifdResis notequal tomissing valuethensetresCodeto(dRes's statusCode())asnumbersetresHeadersto(dRes's allHeaderFields())asrecordelsesetresCodeto0setresHeadersto{}endifreturn{json:aJsonDict,responseCode:resCode,responseHeader:resHeaders}endcallRestPOSTAPIAndParseResultson convRecToJson(aRec)setaDicttocurrent application's NSDictionary's dictionaryWithDictionary:aRecsetjsonDatatocurrent application's NSJSONSerialization's dataWithJSONObject:aDictoptions:(0asinteger)|error|:(missing value)--0 is NSJSONWritingPrettyPrinted setresStringtocurrent application's NSString's alloc()'s initWithData:jsonDataencoding:(current application's NSUTF8StringEncoding)returnresStringendconvRecToJson--multipart/form-data データを作成するon recToMultipartFormDat(aPostData,aUUID)setcrlftoNSString's stringWithString:(stringid13&stringid10)setcrlfcrlftoNSString's stringWithString:(stringid13&stringid10&stringid13&stringid10)setbRestogenerateBundaryTop(aUUID)ofmesetbbRestogenerateBundary(aUUID)ofmesetaDicttocurrent application's NSDictionary's dictionaryWithDictionary:aPostDatasetkListtoaDict's allKeys()aslistsetaBodytocurrent application's NSMutableData's |data|()--set tmpFormStr01 to NSString's stringWithFormat_("Content-Type: multipart/form-data;boundary=\"%@\"%@", bRes, crlfcrlf)--(aBody's appendData:(tmpFormStr01's dataUsingEncoding:NSUTF8StringEncoding))repeatwithiinkListsetaKeyto(contentsofi)setaDatto(NSString's stringWithString:(aDict's valueForKey:aKey))--Boundary From (aBody's appendData:(bbRes's dataUsingEncoding:NSUTF8StringEncoding))(aBody's appendData:(crlf's dataUsingEncoding:NSUTF8StringEncoding))--Form Name fieldsettmpFormStr1toNSString's stringWithFormat_("Content-Disposition: form-data; name=\"%@\"%@",aKey,crlfcrlf)(aBody's appendData:(tmpFormStr1's dataUsingEncoding:NSUTF8StringEncoding))--Form Value field(aBody's appendData:(aDat's dataUsingEncoding:NSUTF8StringEncoding))(aBody's appendData:(crlf's dataUsingEncoding:NSUTF8StringEncoding))endrepeat--Boundary TosettmpFormStr2toNSString's stringWithFormat_("%@--%@",bbRes,crlfcrlf)(aBody's appendData:(tmpFormStr2's dataUsingEncoding:NSUTF8StringEncoding))returnaBodyendrecToMultipartFormDat--macOS 11.0〜11.4ではmacOS側のバグのために実行エラーになるので注意!!on generateBundaryTop(aKey)--最初の部分のためだけに作成returnNSString's stringWithFormat_("Boundary-%@",(aKey))endgenerateBundaryTopon generateBundary(aKey)returnNSString's stringWithFormat_("--Boundary-%@",(aKey))endgenerateBundary結果として、サポートの方にはいろいろお返事をいただいたものの、自分はあきらめてしまいました。
せっかくなので、実際に試そうと思っていた各種活用方法についてまとめることといたします。
DirectCloud-BOX APIの特徴
ユーザー管理とグループ管理、このあたりの機能がAPIで実装されていることが特徴と思われました。
あとは、DirectCloud-BOX API呼び出し時のユーザー認証で「入力された情報が正しくありません。」エラーにハネられまくっていた間に感じたことですが、応答速度がかなり高速。
また、無料コースで試せる容量が5GB。ストレージとして活用しまくるにはすこーし心もとない容量です。
ストレージではなく、ファイルベースのメッセージ伝達システムとして活用
大きなファイルを置くのは(無料コースでは)無理なので、小さなファイルを頻繁に書き込みするような使い方が合っているのだろうと想像しました(実際にそこまで行けていないのですが)。
そこで、複数のクライアント間でクラウドを通じて連携動作を行うための、伝送経路として使えると役立ちそうだと目星をつけました(実際にやってみないと確証は持てないのですが)。
さまざまな情報を示す極小サイズのファイルを書き込み、そのファイルを介して複数クライアント間でRemote Procedure Callをやってみようというわけです。
試作プログラム案1 ローカルじゃんけんプログラム
このファイルベースのメッセージ伝達システムの有効性を検証するために試作を予定していたのが「じゃんけんプログラム」です。まずは、このレベルの簡単なものを作って確認が必要でしょう。
まず最初に、同じLAN上にいるマシン同士でじゃんけんを行えるプログラムを組みます。このさいにも、LAN上にあるファイルサーバーに対して「どの手を出したか」「どのタイミングで出したか(タイムスタンプ)」などの情報をペアにして(たぶんJSONファイル)特定のフォルダに送ります(書き込みます)。
一定時間ごとに(0.5秒ぐらい?)ファイルサーバー上の特定のフォルダの内容を走査し、相手が何か新しい「手」を出していないかを確認します。
試作プログラム案2 リモート多人数じゃんけんプログラム
ローカルじゃんけんプログラムで問題点のあぶり出しができたら、DirectCloud-BOXを利用した「リモートじゃんけん」に移行するつもりでした。おそらく、処理速度の問題や、「ファイルを書き込んだのに(すぐには)検出できない」といった問題を解決する必要があったことでしょう。
この「リモートじゃんけん」プログラムの段階で、DirectCloud-BOXの機能上の問題が出てくることがわかっていました。ファイルベースでメッセージを書き込むため、「古くなったメッセージファイル」はいい感じのタイミングで削除しなくてはなりません。掃除を行う必要があるわけです。この「掃除」をどのタイミングで行うべきなのか、逆に履歴として残しておくべきなのか。そうした検討が行えるものと期待していました。
じゃんけんの「試合」をマッチングした段階で新規フォルダを作成し、そのフォルダへのアクセス権限を参加クライアントに付与。じゃんけんに参加するのは2台のクライアントとは限りません。設定上限まで複数のクライアントが参加できるように試すべきでしょう。
じゃんけんの「試合」が終わったら試合に使ったフォルダごとまるごと削除することで「お掃除」ができるのではないか、という見当をつけていました。
ここまでひととおり作る途中で、おそらく今回のコンテストの応募時間は終わっていたことでしょう。実際には、ユーザー認証のAPI呼び出しの段階で時間が終わってしまったので、「次」に行けなかったのは本当に残念です。
試作プログラム案3 アイデア立案プログラムの複数メンバーによる同時実行
おそらく時間的に「無理」ということになって、ここまで作れないことは最初からわかっていました。でも、最終的に作りたいものや目標もなく、その前段階の試作を行うことは無理です。
そのため、試作プログラム案1&2もこの試作プログラム案3を作るための前哨戦として計画しました。
自分がAppleScriptで開発してMac App Storeで販売しているアイデアプロセッサー「Kamenoko」(→ Mac App Store)。
👁 スクリーンショット 2021-08-24 12.51.09.jpeg
画面上に6角形のセルを書き込み、色分けしたセルとその中に書き込んだ短文によってアイデアをまとめるアプリケーションです。マウスカーソルを移動させると、画面上に6角形のカーソルが描画されます。
選択したセルの内容をWikipediaのREST APIを呼び出して意味的に分解する機能なども持っています(WikipediaのMedia APIは使いやすくてわかりやすく、とてもいいAPIだと思います)。
👁 スクリーンショット 2021-08-24 12.53.55.jpeg
▲「クラウドストレージ」の単語をWikipedia REST API呼び出し機能により、意味的に分解したところ
アウトラインプロセッサやマインドマップなどひととおり試した上で、自分に合うようなプログラムを作ったらこのようなものになっていました。Kamenokoは自分で作って毎日使っており、Twitter上で「#Kamenoko」のタグで検索すると、いろいろアイデアやオンラインミーティングの内容をまとめている様子が見られるようになっています。
1人でこの「Kamenoko」を使ってアイデアをまとめるのもよいのですが、この新型コロナウィルス禍の中、遠隔地にいる相手とこのKamenokoを使ってブレインストーミングができるとよさそうだ、というアイデアを温めていました。
自分のカーソルとは別の色で、画面上に相手のカーソルを描画したいですし、自分が書き込んだ内容に対してコメントが書き込まれるといった味付けも期待したいところです。
ただし、遠隔地間をつなぐのに、どのような方法が実際に使えるか手探り状態でした。
オンラインゲームでREST APIを介して対戦情報をやりとりしている、という話を聞きつけてゲーム系のサービスもいろいろ調べてみたものの、「小さく産んで大きく育てる」ようなことができにくい雰囲気でした。自分にとってはおおげさすぎるように感じられたためです(主に費用面とか導入の手間とか)。
そこに、DirectCloud-BOXでファイルベースのメッセージをやりとりするというプランを思いつき、DirectCloud-BOXの機能を用いてグループ管理やユーザー管理を行い、サービスにログインしている相手と一緒にKamenokoを使ってブレーンストーミングを行うのは、かなりの生産性向上に寄与してくれることになるだろう! と期待しています。
👁 スクリーンショット 2021-08-24 13.12.32.jpeg
単なるビデオ会議とか音声通話では、会議の時間がどんどん伸びてしまう傾向があるように感じていますが、こうしたツールを介してアイデアや意図を共有できると、時間の節約や生産性の向上をより多くの組織で実現できるに違いありません。
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme
