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年9月9日月曜日
SQLをつかってJSONで格納されたデータを検索してみた
こんにちは!
シタテルでエンジニアをしている建山です。
今回は、データベース(MySQL)にJSON形式で格納されているデータをSQLで検索する方法を紹介します。
シタテルでは、AWS上のデータベースに蓄積されたデータをredashというツールを使い、SQLでデータ抽出したあと、データ分析できるようになっています。
やりかた
今回はMySQLの
たとえば、格納データが以下の2レコードあったとします。
{"maxCount":1000,"targets":{"ladies":false,"mens":true,"kids":false,"baby":false}}
{"maxCount":50,"targets":{"ladies":true,"mens":false,"kids":false,"baby":false}}
ここで、maxCountが50のデータを検索したいときは、
JSON_EXTRACTを使用し、以下のように抽出できます。(テーブル名:table_a、JSON格納カラム名:detail)
select * from table_a where JSON_EXTRACT(detail, '$.maxCount') = 50
targetsのladiesがtrueの検索をしたいときは、以下のようになります。
select * from tablename where JSON_EXTRACT(detail, '$.targets.ladies') = true
# 感想
このように、```$.
のあとに、キーの名前を指定してあげれば、かんたんに、jsonが格納してあるカラムの中身を検索することができます。(表示の記述方法も同じ)
公式のドキュメントはこの辺あたりのようです。
https://dev.mysql.com/doc/refman/5.7/en/json.html
他にもいくつか関数があるようなので、いろいろ試してみたら、いいSQL+JSONライフが送れるかも知れません!
簡単ではありましたが、ご紹介でした!
RPA触ってみた
どうもお久しぶりです。シタテルの朝野です。
突然ですがRPAってご存知でしょうか?私は数週間前にまともに知りました🙄
"Robotic Process Automation" の略で、PC上での作業をソフトウェアロボットが行うよう自動化することです。
現在私のいるDevOpsチームでは単純な作業を自動化して時間と手間を節約できないかという取り組みを少しずつ進めています。
そんな中でRPAというものの存在を知り、簡単に無償で導入できるものを試してみたので、サンプルと共に紹介します。
今回やってみたサンプルは、シタテルの「マイアトリエ」をブラウザで開いてログインするというものです。
(※サンプル内のメールアドレスとパスワードはダミーの値です)
Javascript for Automation
MacOS Yosemite以降で使用可能なスクリプト言語です。
サンプルコードはこのようになります。
const DELAY = 3;
const Chrome = Application("Google Chrome");
const window = Chrome.windows[0];
const tab = Chrome.Tab({
url: 'https://atelier.sitateru.com/login'
});
window.tabs.push(tab);
delay(DELAY);
inputText(tab, 'email', 'sample@sitateru.sample');
inputText(tab, 'password', 'sample_password');
submit(tab);
function inputText(tab, elementId, value) {
tab.execute({ javascript: "document.getElementById('" + elementId + "').value =`" + value + "`;" });
}
function submit(tab) {
tab.execute({ javascript: "document.forms[0].submit();" });
}
ブラウザ上での入力やクリックはjavascriptをブラウザ内で走らせることで行っています。
実行はコマンドで $ osascript -l JavaScript sample.js
と打つだけです。
JavaScriptを書いてコマンド実行なので、RPAわからんけどJavaScriptチョットワカル!という私のような者にはありがたいです。
エンジニアで使う分にはハードルが低いと思うのですが、ノンプログラマーやMac使わない人がいると導入しにくそうなのが難点でしょうか。
SikuliX
JavaがあればWindowsでもMacでもLinuxでも使えるOSSのRPAアプリケーションです。
IDEを立ち上げて自動化プログラムを作成・実行します。
サンプルコード(コードと言っていいのかわかりませんが)はこのようになります。
自動化したい操作をスクリプトににしてIDE上で書いていきますが、マウスポチポチでもある程度作れるのでノンプログラマーにもいくぶんとっつきやすそうです。
画像処理で有名なOpenCVを使って作られているらしく、操作対象の認識を「デスクトップ画面の中で対象の画像を抽出する」ことで実現しています。
(サンプルコードの click()
のカッコ内にあるのは操作したい要素のスクリーンショットなのです)
画面上の文字を認識するOCR機能も一応あるのですが試してみたところ精度が怪しく実用には難しいなという印象でした。
機能面はやや物足りない感触でしたが、OSをまたいで使えることと導入の難易度が低そうなのはメリットですね。
UiPath
こちらもIDEで作成・実行するタイプで、GUIでフローチャートを組み立てたりブラウザ操作を自動で読み取って操作を記録したりと、基本シェアウェアだけあって操作感や機能は充実しています。(そのぶん見た目は複雑なので最初は少し戸惑うかも)
個人や中小企業はCommunity Editionという無料版が使えますが、ある程度以上の規模の企業で使う場合は有料になるようです。
https://www.uipath.com/ja/freetrial-or-community
サンプルコード(これもコードと言っていいのかわかりませんが)はこのようになります。
作業の流れがグラフィカルに表示されるので大掛かりなようにも見えますが、Webページ上の要素を選択して操作しているというのは同じですね。
公式コミュニティがあるほかハンズオンセミナーなども開かれているようで、費用面とWindowsのみという点が問題なければ有力な候補になりそうだと思います。
というわけで、今回は無料で使えるRPAを簡単にお試ししてみました。
こういったRPAは使いようで色々な作業の手間を省けそうな予感がしているので、できることを把握してうまく活用していきたいですね。
この記事がRPAに手を出してみる取っ掛かりになれば嬉しいです🤗
2019年6月3日月曜日
書評『進化的アーキテクチャ ―絶え間ない変化を支える』
どういう本か・総評
適応度関数
アーキテクチャの分類・マイクロサービス
- モノリス(3層レイヤ化モノリス等を含む)
- イベント駆動アーキテクチャ
- SOA
- マイクロサービス
- サーバレス
モノリスを構築できないとき、なぜマイクロサービスがその答えだと思うのか。
マイクロサービスという名前にひっかからないようにしてほしい。各サービスは決して小さい必要はない。むしろ有効な境界づけられたコンテキストを捉えることが必要なのだ。
サービス境界分割の手法
- ビジネス機能グループ
既存のビジネスコミュニケーション階層を反映する。コンウェイの法則に従う。 - トランザクション境界
トランザクションは最も分離しにくい要素である。 - デプロイメント目標
デプロイ頻度が異なる箇所を境界とする。
コンウェイの法則/逆コンウェイ戦略
システムを設計するあらゆる組織は、必ずその組織のコミュニケーション構造に倣った構造を持つ設計を生み出す。
ゆえに、チーム外の人間が所管する業務フローやコードにはできるだけ影響を及ぼさないように設計・実装しようとする(調整範囲をチーム内に閉じようとする)力学が働く。
結合よりも重複
マイクロサービスは無共有アーキテクチャを形成する。その目標は、できるだけ結合を減らすことだ。一般的に、結合よりも重複の方が望ましい。
ツールがあまりにもコードの再利用を容易にしてしまったせいで、現代の開発者は、たやすく結合できてしまう環境の中で適切な結合を行うことに悪戦苦闘している。
例えば、 CatalogCheckout サービスと ShipToCustomer サービスの両方が Item という概念を持つ。両方のチームが同じ名前と同じプロパティを持つので、開発者はサービスをまたいでそれを再利用しようと試みる。それが時間や労力の節約につながると考えるからだ。
けれど、それは労力を増やす結果となる。なぜなら、コンポーネントを共有する全てのチームが変更を伝搬しなければならなくなるからだ。一方、コンポーネントを結合せずに各サービスにItem があり、必要な情報だけをCatalogCheckout から ShipToCustomer に渡す場合は、それらは独立して変更することが可能だ。
開発者が再利用可能なコードを作成する場合、開発者は最終的にコードを使用する無数の方法に対応するための機能を追加する必要がある。その将来の保証全ては、開発者がコードを単一の目的のために使用することをより難しくする
腐敗防止層を設ける
デプロイに関するプラクティス
取っ掛かりをつかみたい人にはおすすめできるかと思います。
2019年5月8日水曜日
プログラミング・ゲーム
シタテルでバックエンドエンジニアをしている熊谷です。
今日は、プログラミングを学べるゲームやアプリの紹介をしたいと思います。
まず1つ目は、「Swift Playgrounds」です。
これはAppleが出しているiPad用のアプリで、キャラクターを動かすなどのプログラミングを、ゲーム感覚で学ぶことができます。
アプリ自体は前からあったのですが、最近やっと少し触ることができました。
画面イメージとしては、こんな感じです。
2019年4月23日火曜日
同時に複数のマシーンで同じ操作をする with Tmux
sitateruのエンジニア北爪です。
複数のシェルで同じ操作をしたい時ってないですか?
- ログを一箇所に集めてないけど、アプリケーションサーバーが複数台ある
- 複数台同時にtopコマンドで負荷をまたはtailコマンドでログを調査したい
など調査段階でAnsible, Fabやcapistranoで操作するほどではなく、複数のサーバーを同じようにインタラクティブに操作したいという要求はあるように思います。
今回は複数のシェルに対して、分割された画面をみなが同時に操作、そのコマンドラインの結果を参照することが可能です。
tmuxを風にいうと、一つのpaneに行うキーボード操作をそのpaneが存在しているWindow内すべてのpaneキーボード操作を同期することができます。
この機能は、tmuxの synchronize-panes 機能です。tmuxを使ってない人には導入する大きなメリットの一つになるのではないかとお思います。デフォルトです。
http://man.openbsd.org/OpenBSD-current/man1/tmux.1#synchronize-panes
使い方
1. tmuxを起動し、複数のpaneを作成がある状態で2. tmuxにbindしてあるtmuxのprefixキーを入力した後で、 `:` を入力します
(余談私は ctrl-t にしています)
3. tmux用のコマンドが立ち上がるので、そこに、
set synchronize-panes on
を入力します。4. そうするとなんと!表示されているpane(分割した画面) に同じキーボード操作が入力されます
5. もう同期したくなくなったら、
set synchronize-panes off
にて解除することが可能です。以上です。
2019年4月16日火曜日
Vue.jsとScoped CSSとz-indexの話

こんにちは、シタテルの茨木です。
突然ですが、scoped cssいいですよね
scoped cssは、特にVue.jsにおいては、css設計のスタンダードと言っていいのではないかと思います。BEM等の学習コストの高い手法を覚える必要がなく、「コンポーネント内でのみcssクラス名の一意性を確保すればいい *1」という、シンプルで管理しやすい設計指針を打ち立てることができ、実に理にかなった手法ですね。
プロジェクト全体でのcss命名規約の統制問題は依然としてありますが、コンポーネントベースでの設計手法自体がコンポーネントを書き捨てる運用と親和性があるため、そこまで大きな問題にはならないのかなと思います。
依然として残る考慮点
一方、scoped cssだけで、コンポーネント内にcss的な関心事を完全に閉じ込められるかというとそうではなく、当然ながら例外もあります。最も代表的な例としては、cssの仕様上継承されてしまう属性ですね。
font-sizeなど、属性自体が子孫要素に継承されるものとして定義されていますので、セレクタレベルでscopedにしたところで、子孫要素への影響を避けることはできません。
まあこの辺は書いていても直感的に理解しやすいのでそこまで問題になりにくいのですが、個人的に危ないなと思うのは「z-index」「重ね合わせコンテキスト」です。
z-indexの管理
cssをざっくり理解したつもりになっているエンジニアが犯しがちな間違いの一つが、z-indexがグローバルだと思いこんでしまうことだと思います。そう考えているエンジニアは、Vueプロジェクト全体で単純にコンポーネントごとのz-indexを管理することをを思いつきます *2。例えば、コンテキストメニューのコンポーネントはz-index1000番台、モーダルダイアログのコンポーネントはz-index500番台といった管理です。(モーダル上でコンテキストメニューを開くかもしれない。その場合はコンテキストメニューがモーダルの下に回り込んでほしくない、なんてことを考えているわけですね)
この方式は重ね合わせコンテキストがページ内で1つであるうちは上手く機能しますし、ルール自体はおそらく間違いでもないです。
ただ、重ね合わせコンテキストに関する考慮を漏らしていると、「z-indexで勝っているはずなのに、なぜか後ろに回り込んでしまう要素」が出てくる可能性があり、「なぜか動かない」という状況に見舞われます。実際には仕様理解が足りていないだけなのですが。
理解すべき仕様「重ね合わせコンテキスト」の詳細についてはMDNを読んでください。
https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
ざっくり言えば、「z-indexは同じ重ね合わせコンテキストの中での比較にしか使われない」「重なる系の属性(potision: absoluteなど)が指定された要素は自身が重ね合わせコンテキストを形成する」ということです。
これもfont-sizeなどと同じで、親要素で指定された属性が(たとえscopedであろうとも)、子孫要素に影響を与えうる、ということですね。
デザインは文脈依存?
scoped cssとコンポーネントベースでの部品化は、確かに今までのcssの不便さや複雑さの大部分を解決しているように見えますが、z-indexの件を一つの例として考えると、「デザイン・見た目・UIは、どこまで部品化しても文脈依存から逃れられない」ということを示している気がします。まさに、重ね合わせ「コンテキスト」ですしね。z-indexをpropsで渡せば…みたいな発想はもちろんあるのですが、結局その先にあるのはinput要素の再発明だったりするわけです。input要素をwrapしてたはずがinput要素を作っていた…みたいな。
どんなコンテキストでもデザイン破綻せず使うことのできるUI部品の実装は、相当な労力を要します。(まあ、実際にUI部品系のnpmパッケージの中を覗いてみると、普通にz-indexがハードコードされていて変更できなかったりすることが多く、みんなほどほどに手を抜いているんだと思いますが)
プロジェクト内でのデザイン統一・再利用を想定してコンポーネントを分割設計する際に、デザイン的な汎用性を求めるのはほどほどにしておくべきなのかな、と思った次第でした。
他コンポと疎結合に作るとか、ステートレスに作るとか、そちらに労力を割いたほうが実りが大きいんじゃないかな。UIの寿命は短いので、捨てやすいように疎結合に書くのが一番大事かな、と個人的には思います。
*1: slot内のスコープが親子共通だったり、コンポーネントroot要素に親からclassが注入できるといった例外はありますが
*2: 私です