clasp + typescriptでAPIを作ってみる
はじめに
GoogleAppsScriptがほぼjsなのにes6でかけなかったり、
tsで書けなかったりちょっと残念だなとか思ってたら公式でサポートされてた。
今回はclaspを使ってtypescriptで書いてAPIを公開してみる。
TL;DR.
インストールする
$ npm install --save-dev @google/clasp $ clasp -v >1.7.0
インストールはこれで完了。
globalにインストールしない場合はフォルダ構成に合わせてpathを通してください。
※macならbash_profile
に下記追記でOK。
export PATH=$PATH:./node_modules/.bin
プロジェクト作成
# ブラウザが開くと思うので任意のGoogleアカウントでログイン $ clasp login # ソース置いとくフォルダ作成 $ mkdir src # rootDirを"src"に指定して"sample"という名前のプロジェクト作成 $ clasp create --title "sample" --rootDir ./src # どれか聞かれるから好みのものを選ぶ(今回はapi) # APIはデフォルトだとエラーになるので下記URLから事前に「オンにしておく」 # https://script.google.com/home/usersettings >? Clone which script? > standalone > docs > sheets > slides > forms > webapp >❯ api # サンプルのプログラムを作成 # claspではES6もTypeScriptも問題ない $ touch src/sample.ts
これでプロジェクトの作成は問題ないはず。
サンプルプログラムは下記の通りにした。
(と言っても公式のサンプル丸コピだけど)
// Optional Types let isDone: boolean = false; let height: number = 6; let bob: string = "bob"; let list1: number[] = [1, 2, 3]; let list2: Array<number> = [1, 2, 3]; enum Color {Red, Green, Blue}; let c: Color = Color.Green; let notSure: any = 4; notSure = "maybe a string instead"; notSure = false; // okay, definitely a boolean function showMessage(data: string): void { // Void console.log(data); } showMessage('hello'); // Classes class Hamburger { constructor() { // This is the constructor. } listToppings() { // This is a method. } } // Template strings var name = 'Sam'; var age = 42; console.log(`hello my name is ${name}, and I am ${age} years old`); // Rest arguments const add = (a, b) => a + b; let args = [3, 5]; add(...args); // same as `add(args[0], args[1])`, or `add.apply(null, args)` // Spread operator (array) let cde = ['c', 'd', 'e']; let scale = ['a', 'b', ...cde, 'f', 'g']; // ['a', 'b', 'c', 'd', 'e', 'f', 'g'] // Spread operator (map) let mapABC = { a: 5, b: 6, c: 3}; let mapABCD = { ...mapABC, d: 7}; // { a: 5, b: 6, c: 3, d: 7 } // Destructure map let jane = { firstName: 'Jane', lastName: 'Doe'}; let john = { firstName: 'John', lastName: 'Doe', middleName: 'Smith' } function sayName({firstName, lastName, middleName = 'N/A'}) { console.log(`Hello ${firstName} ${middleName} ${lastName}`) } sayName(jane) // -> Hello Jane N/A Doe sayName(john) // -> Helo John Smith Doe // Export (The export keyword is ignored) export const pi = 3.141592; // Google Apps Script Services var doc = DocumentApp.create('Hello, world!'); doc.getBody().appendParagraph('This document was created by Google Apps Script.'); // Decorators function Override(label: string) { return function (target: any, key: string) { Object.defineProperty(target, key, { configurable: false, get: () => label }); } } class Test { @Override('test') // invokes Override, which returns the decorator name: string = 'pat'; } let t = new Test(); console.log(t.name); // 'test'
pushしてみる
# rootDirに指定したフォルダの内容を全てpush $ clasp push └─ src/appsscript.json └─ src/sample.ts Pushed 2 files.
pushされたprojectを確認すると下記のようなgsに変化される。
var exports = exports || {}; var module = module || { exports: exports }; var __assign = (this && this.__assign) || Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; // 型定義 var isDone = false; var height = 6; var bob = "bob"; var list1 = [1, 2, 3]; var list2 = [1, 2, 3]; var Color; (function (Color) { Color[Color["Red"] = 0] = "Red"; Color[Color["Green"] = 1] = "Green"; Color[Color["Blue"] = 2] = "Blue"; })(Color || (Color = {})); var c = Color.Green; var notSure = 4; notSure = "maybe a string instead"; notSure = false; // okay, definitely a boolean function showMessage(data) { // Void Logger.log(data); } showMessage("hello"); // クラス var Hamburger = /** @class */ (function () { function Hamburger() { // コンストラクタ } Hamburger.prototype.listToppings = function () { // メソッド }; return Hamburger; }()); // テンプレート文字列 var name = "Sam"; var age = 42; console.log("hello my name is " + name + ", and I am " + age + " years old"); // Rest arguments var add = function (a, b) { return a + b; }; var args = [3, 5]; add.apply(void 0, args); // same as `add(args[0], args[1])`, or `add.apply(null, args)` // スプレッド構文 (array) var cde = ["c", "d", "e"]; var scale = ["a", "b"].concat(cde, ["f", "g"]); // ['a', 'b', 'c', 'd', 'e', 'f', 'g'] // スプレッド構文 (map) var mapABC = { a: 5, b: 6, c: 3 }; var mapABCD = __assign({}, mapABC, { d: 7 }); // { a: 5, b: 6, c: 3, d: 7 } // 分割代入 var jane = { firstName: "Jane", lastName: "Doe" }; var john = { firstName: "John", lastName: "Doe", middleName: "Smith" }; function sayName(_a) { var firstName = _a.firstName, lastName = _a.lastName, _b = _a.middleName, middleName = _b === void 0 ? "N/A" : _b; console.log("Hello " + firstName + " " + middleName + " " + lastName); } sayName(jane); // -> Hello Jane N/A Doe sayName(john); // -> Helo John Smith Doe // Export (The export keyword is ignored) exports.pi = 3.141592; // Google Apps Script の独自サービスの利用 var doc = DocumentApp.create("Hello, world!"); doc .getBody() .appendParagraph("This document was created by Google Apps Script."); // デコレータ(高階関数) function Override(label) { return function (target, key) { Object.defineProperty(target, key, { configurable: false, get: function () { return label; } }); }; } var Test = /** @class */ (function () { function Test() { this.name = "pat"; } __decorate([ Override("test") // invokes Override, which returns the decorator ], Test.prototype, "name"); return Test; }()); var t = new Test(); console.log(t.name); // 'test'
APIを作ってみる
とりあえず今回は外部から叩けることを確認する。
src/api.ts
としてファイルを作った。
中身は下記の通り。ただ単に適当なJSONを返した。
// doGetはGETで叩かれたときに実行される関数 function doGet() { const resData = JSON.stringify({ message: 'Hello World!' }); // ContentServiceを利用して、responseを作成 // そのまま返してもエラーになる ContentService.createTextOutput(); const output = ContentService.createTextOutput(); output.setMimeType(ContentService.MimeType.JSON); output.setContent(resData); // return response-data return output; }
APIを使えるように設定する
「公開 > 実行可能APIとして導入」からAPIは初公開するとき警告出るけどここではスルーして続行。
スルーするとAPIIDが出てくるのでコピっておく。
スクリプトにアクセスできるユーザは「全員」にしておく。
次に「公開 > ウェブアプリケーションとして導入」から設定する。
プロジェクトバージョンは先程公開したAPIのバージョン、
次のユーザーとしてアプリケーションを実行は「自分」、
アプリケーションにアクセスできるユーザーは「全員(匿名ユーザを含む)」にして導入。
導入すると「現在のウェブ アプリケーションの URL」が出てくるからコピっておく。
APIを叩いてみる
ブラウザから直接叩くかcurl -L 【コピったURL】
でOK。
{"message":"Hello World!"}
みたいなレスポンスが帰ってくれば成功。
まとめ
今回はclasp + typescriptでGoogle Apps ScriptのAPIを作ってみた。
見た目はtsだったりするけど、中身はGASなのでちゃんとスプレッドシートとかGmailとかと連携はできるはず。
Googleのなんかを使うのはまたいずれ。
それでは今回はこの辺で。