2019年10月11日金曜日
GASでG Suiteグループ追加をやってみた
以前の記事で、作業を楽にするためにRPAを触ってみたという話を書いたのですが
その関係の取り組みの一つとして現在Google Apps Script(GAS)でアカウント管理操作を集約・省力化するという作業を進めています。
Google Apps Scriptはご存知の方も多いと思いますが、Googleの様々なサービスと連携させて動かすことができるスクリプト言語です。
シタテルではG Suiteをバリバリ使っているので、アカウント管理システムもGASを中心として作っていこうと考えています。
そこで、まずはGASで何か作ってみる第1号として、ユーザーをグループに追加するフォームを作ってみました。
グループ追加はG Suiteの管理画面からできるのですが若干作業として面倒だったりするんですね。
というわけでまずはGoogleフォームを作成。
「追加するユーザー」「追加先グループ」の入力欄を作ります。
それからオプション設定の「メールアドレスを収集する」をチェック。
つづいて右上メニューから「スクリプトエディタ」を選択するとGASのオンラインエディタが開きます。
ここでソースコードを編集します。
今回のコードはこのようなものです。
var privilegedRespondent = ['taro@sitateru.sample', 'jiro@sitateru.sample'];
// グループ追加,フォーム送信時に実行
function addUserToGroup(event) {
console.log('addUserToGroup()');
var message = "グループ追加フォームからの通知です\n\n";
const respondent = event.response.getRespondentEmail();
if(privilegedRespondent.indexOf(respondent) === -1) {
console.log('Respondent ' + respondent + ' not allowed');
message += 'あなた( ' + respondent + " )にはアカウントを作成する権限がありません\n";
sendResult(respondent, message);
return;
}
const response = parseResponse(event.response.getItemResponses());
const userName = response['追加するユーザー'];
const groups = response['追加先グループ'].split(',');
for (var i = 0; i < groups.length; i++) {
var groupName = groups[i].trim();
try {
var member = addGroupMember(userName, groupName);
console.log(' added member', member);
if(member == undefined) {
message += "ユーザーをグループに追加できませんでした。入力内容を確認してください\n";
message += ' ユーザー: ' + userName + "@sitateru.com\n";
message += ' グループ: ' + groupName + "@sitateru.com\n\n";
} else {
message += "ユーザーをグループに追加しました\n";
message += ' ユーザー: ' + userName + "@sitateru.com\n";
message += ' グループ: ' + groupName + "@sitateru.com\n\n";
}
} catch(error) {
console.log(' error', error);
message += "ユーザーをグループに追加できませんでした。入力内容を確認してください\n";
message += ' ユーザー: ' + userName + "@sitateru.com\n";
message += ' グループ: ' + groupName + "@sitateru.com\n";
message += ' エラー: ' + error.toString() + "\n\n"
}
}
sendResult(respondent, message);
}
// Googleユーザーをグループに追加
function addGroupMember(userName, groupName) {
if(userName && groupName) {
const userEmail = userName + '@sitateru.com';
const groupEmail = groupName + '@sitateru.com';
console.log(' add ' + userEmail + ' to ' + groupEmail);
const member = {
email: userEmail,
role: 'MEMBER'
};
return AdminDirectory.Members.insert(member, groupEmail);
}
}
// メール送信
function sendResult(recipient, message) {
MailApp.sendEmail({
name: 'アカウント管理システム',
to: recipient,
subject: 'グループ追加フォーム実行結果',
body: message
});
console.log('send result');
}
// フォームの入力内容をオブジェクトにして返す
function parseResponse(itemResponses) {
var obj = {};
for (var i = 0; i < itemResponses.length; i++) {
var itemResponse = itemResponses[i];
var question = itemResponse.getItem().getTitle();
// var type = itemResponse.getItem().getType();
var answer = itemResponse.getResponse();
obj[question] = answer;
}
return obj;
}
あとはスクリプトにグループを操作する権限を与えるために、メニューの「Googleの拡張サービス」を選択して「Admin Directory API」をONにします。
今回のコードではグループ追加フォームとして実用上あったほうがいい機能もいろいろつけてみました。
・
event.response.getRespondentEmail()
でフォーム送信者のメールアドレスを取得し、あらかじめ定義しておいた privilegedRespondent
にいる人でなければ実行しない・「追加先グループ」はカンマ区切りで複数指定できるように
・最後に実行結果をメール送信
MailApp.sendEmail()
と、こんなもので実際に利用を始めています。
こちらにリファレンスがありますが、GASはかなりの種類のGoogleのサービスを操れるのが強みです。↓のページで左メニューの「G Suite Services」や「Advanced Google Services」を開いてみよう!
https://developers.google.com/apps-script/reference
これからもいろいろなものを作って楽をしていこうと思います!
2019年2月18日月曜日
Google Cloud Functionsで画像を自動リサイズする
こんにちは、DevOpsチームの甲斐です。
今回は、Google Cloud Functions(以下、GCF)で画像を自動リサイズする手順を紹介したいと思います。
GCFとはAWSでいうところのLambdaです。いわゆる、サーバーレスってやつですね。
最近はGCPを触ることが多いのですが、先日GCP上のWordPressのアップロード画像をリサイズするために、
GCFを使って自動的に画像をリサイズするようにしました。
AWSのLambdaも触ったことありますが、GCFのほうが手間が軽い印象を持ちました。
まだGCFをお使いになったことがない方は、ぜひご参考にしていただければと思います。
前準備
GCFを使う上で必要な環境は以下のとおりです。
- GCPプロジェクトの作成
- 課金の有効化
- Cloud Functions APIの有効化
- Cloud SDKのインストール
詳細は、以下に書かれていますので、こちらをご参照ください。
https://cloud.google.com/functions/docs/quickstart-console?hl=ja
今回の仕様
今回の仕様は以下のとおりです。
- Google Cloud Storage(以下、GCS)の当該バケットのuploadsディレクトリにアップロードされた画像を自動的にリサイズ
- リサイズする画像フォーマットはimage/jpegのみ
- オリジナル画像は".orig"拡張子をつけてバックアップ
- リサイズされた画像はオリジナルの画像と同じ名前で上書き
実装
それでは、早速実装の手順を紹介していきたいと思います。
今回はNode.js v8で書きました。それ以外にもNode.js v6, Python(beta), Go(beta)が使えます。
1. 作業ディレクトリの作成
mkdir -p gcf/convert_image
cd !$
2. npm init
npm init
3. 必要なパッケージをインストール
今回は、@google-cloud/storage(GCS関連パッケージ)とgm(画像編集パッケージ)をインストールします。
npm install @google-cloud/storage gm --save
4. コーディング
コードは以下のとおりです。
最初に少しハマったところとしては、リサイズした画像でオリジナル画像を上書きすると
再びイベントが発火されるので無限ループに陥ってしまったことです。
対策としてメタデータにコンバート済みである旨を記述し、処理の最初にメタデータをチェックすることで判断するようにしました。
'use strict';
const gm = require('gm').subClass({imageMagick: true});
const fs = require('fs');
const path = require('path');
const {Storage} = require('@google-cloud/storage');
const storage = new Storage();
exports.convertImage = data => {
if (data.resourceState === 'not_exists') {
console.log(`Skip because not_exists`);
return;
} else if (!data.name) {
console.log(`Skip because no name`);
return;
} else if (!data.name.startsWith('uploads')) {
console.log(`Skip because not uploads: ${data.name}`);
return;
} else if (data.name.endsWith('.orig')) {
console.log(`Skip because original image: ${data.name}`);
return;
} else if (data.contentType !== 'image/jpeg') {
console.log(`Skip because not image/jpeg: ${data.contentType}`);
return;
}
const file = storage.bucket(data.bucket).file(data.name);
console.log(`Uploaded image : ${file.name}`);
const tempLocalPath = `/tmp/${path.parse(file.name).base}`;
return file
.getMetadata()
.then(data => {
console.log('[Check already converted or not]');
const metadata = data[0];
if (metadata.metadata && metadata.metadata.isConverted) {
console.log(`${file.name} is already converted.`);
Promise.reject();
} else {
console.log(`${file.name} is not yet converted.`);
Promise.resolve();
}
})
.catch(err => {return;})
.then(() => {
console.log('[Download file]');
return file
.download({destination: tempLocalPath})
.catch(err => {
console.error('Failed to download file.', err);
return Promise.reject(err);
});
})
.then(() => {
console.log('[Backup a image]');
return file.copy(`${file.name}.orig`);
})
.then(() => {
console.log('[Convert a image]');
return new Promise((resolve, reject) => {
gm(tempLocalPath)
.samplingFactor(2, 2)
.strip()
.quality(85)
.interlace('Line')
.colorspace('sRGB')
.write(tempLocalPath, (err, stdout) => {
if (err) {
console.error('Failed to convert image.', err);
reject(err);
} else {
resolve(stdout);
}
});
});
})
.then(() => {
console.log('[Upload a converted image]');
const options = {
destination: file,
resumable: false,
metadata: {
metadata: {
isConverted: true
}
}
};
return file.bucket
.upload(tempLocalPath, options)
.catch(err => {
console.error('Failed to upload a converted image.', err);
return Promise.reject(err);
});
})
.then(() => {
console.log('[Remove a temporary file]');
return new Promise((resolve, reject) => {
fs.unlink(tempLocalPath, err => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
};
5. デプロイ
デプロイは以下のようにします。
今回の場合、runtimeはnodejs8になります。また、メモリやタイムアウトもオプションで設定できます。
gcloud functions deploy <name> --entry-point <entry-point> --runtime <runtime> --trigger-bucket <trigger-bucket> --region <region> [--memory <memory> --timeout <timeout>]
デプロイが完了したら、以下のコマンドで正常に登録されたことを確認します。
gcloud functions list
6. テスト
サンプル画像を当該バケットにアップロードし、正常にリサイズされることを確認します。
% gsutil cp sample.jpg gs://<bucket>/uploads/
% gsutil ls -l "gs://<bucket>/uploads/sample.jpg*"
2601078 2019-02-14T02:37:45Z gs://<bucket>/uploads/sample.jpg
14583723 2019-02-14T02:37:43Z gs://<bucket>/uploads/sample.jpg.orig
TOTAL: 2 objects, 17184801 bytes (16.39 MiB)
まとめ
簡単ですが、GCFで画像をリサイズする実装手順を紹介しました。
今回はGCSをトリガーにしていましたが、それ以外にもGoogle Cloud Pub/SubやHTTPリクエストをトリガーにすることも出来ます。
GCFの詳細については本家ドキュメントをご参照ください。
https://cloud.google.com/functions/docs/?hl=en
また、以下のGitリポジトリにGCFのサンプルプログラムがありますので参考にしてみてください。
https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/master/functions
2018年11月16日金曜日
githubイシュー/PRの管理にjasperを導入したら幸せになった話
こんにちは!
シタテルで エンジニアをしている建山です。
主に工場向けのマイオペというシステムの開発を行っています。
シタテルではgithubを使ってソースコード・イシュー/PR管理などをしています。
その中で、困るなとおもっていたのがイシューへのアサインやコメントに気づく仕組みでした。
PRやイシューで自分にアサインやコメントされているにもかかわらず、気づくかない、気づくのが遅くなるなど、あげくのはてに、slackでメンションもらったりということもありました。
そこで、リアルタイムに通知を受け取ったり、メンションにもれなく気づけるようになりたいとおもっていたところ、社内のエンジニアにjasperよさそう!とおしえていただいて実際便利でしたので紹介します。
jasper以外にもいい方法はいくつかあるとおもいますが、今回はjasperを使った方法を記載します。
jasperとは
githubのイシュー/PRを閲覧や管理をしやすくするソフトウェアです。
Mac版/Windows版/Linux版があります。
https://jasperapp.io/
cookpadのエンジニアの方がつくられているようです(https://techlife.cookpad.com/entry/2017/03/14/100000
)
ほとんどここに書かれています。
jasperを使った方法
jasperにはstreamsを登録する機能があります。こちらに登録しておくと、
条件ごとに一覧で見ることができて、通知を受け取りたい場合は、特定のstreamだけ通知を受け取るということもできます。
私が通知を設定しているstreamsはこちら。
公開中のイシューで自分にメンションもらっているリスト
is:issue is:open mentions:ユーザー名
公開中のイシューで自分に レビューリクエストもらっているリスト
is:open is:pr review-requested:ユーザー名
PRで自分にアサインもらっているリスト
is:open is:pr assignee:ユーザー名 archived:false
公開中のPRで自分にメンションもらっているリスト
is:pr is:open mentions:ユーザー名
まだ使いこなせていないのですが、ほかにもたくさんの用途にstreamを分けることができます。
https://jasperapp.io/doc.html#stream
個人的には、jasperを使いだして、github上でするべきコメントを、slack上でやることが減ったと感じています。github上にもコメントで経緯ものこるので、いいなと感じています。
日々、改善しながらやっていきたいと思います!