intra-martの隠しコマンド?

はじめに

みなさまこんにちは、またしてもマサキです。
外は随分と涼しくなってきました。早々秋物に着替えた方も多いのではないでしょうか。
9月も半分を回り、平成最後の年末が見えてくる頃合です。
これからも何かと『平成最後の××』が盛りだくさんですので、張り切っていきましょう。

さて今回は、intra-martのワークフロー開発で役に立つ豆知識を一つご紹介します。
intra-mart技術者、特にintra-mart開発を最近始めた方向けのテーマです。

【前置き】システム案件IDとユーザデータID

初めてintra-martのワークフローに触れるという方が、まずぶつかるがこの聞き慣れない単語『システム案件ID』ではないでしょうか。
さらに『ユーザデータID』というものもあって、頭の中でごっちゃになりやすい要素ですね。

いずれも、ワークフローを起票・申請したとき、その案件を特定するために用いられる一意のIDです。
『システム案件ID』と『ユーザデータID」は1:1の関係にあり、 どちらか一方だけあれば、案件について欲しい情報を引き出すことができます。

このとき、それぞれの用途は以下のようになっています。

  • システム案件ID(SystemMatterId):ワークフロー側が採番し、システム内部でのみ利用するID
  • ユーザデータID(UserDataId):ユーザコンテンツ側が任意のタイミングで採番し、業務データとの関連付けなどに用いるID

二つのIDの関係イメージ
二つのIDの関係イメージ

システム開発において、ワークフローと連携する業務テーブルの外部キーとして用いるのは『ユーザデータID』である、と覚えておけば、ひとまず問題ありません。

逆に、intra-martがもともと持っているテーブルからデータを取得する場合には『システム案件ID』をキーとして利用します。
APIの引数などでしばしば要求されるかと思われます。

システム案件IDを知りたい

システム案件IDがあれば、案件の情報はなんでも取得できます。
しかし、intra-martの標準画面を見る限り、システム案件IDという項目は見当たりません。
もちろんデータベースをのぞけば分かりますが、それではあまりに手間ですね。

そこで今回は、簡単にシステム案件IDを確認する方法をご紹介します。

intra-martの隠しコマンド

intra-martの標準画面である『未処理一覧画面』を開いてください。

未処理一覧画面
未処理一覧画面
この状態で、キーボードから『Ctrl + Shift + i』を入力してみましょう。
隠しコマンド入力後イメージ
隠しコマンド入力後イメージ
一覧の一番左に、システム案件IDを表示する列が現れました。
また、『Ctrl + Shift + o』を押すと元に戻ります。

簡単にシステム案件IDを確認できましたね!
これは開発の中で非常に重宝するのではないでしょうか。
同じコマンドを使って、他の一覧(一部を除く)でも同様の操作が可能です。

intra-martの隠しコマンド?

このコマンドは、intra-martをご存じな方でも、あまり知る機会のないものではないでしょうか。
隠しコマンドと言いつつ、実は公式ドキュメントにも載ってはいるのですが、
有用性の割に、トラブルシューティングの片隅にTipsとして記されているのみです。

intra-mart IM-Workflow トラブルシューティング
https://www.intra-mart.jp/download/product/iap/im_workflow/im_workflow_troubleshooting/texts/assemble_information/index.html#tips

それから、確認した限りでは、intra-mart開発者ブログの昔の記事にて紹介されていたりしました。

intra-mart Developer Site
https://dev.intra-mart.jp/intra-mart_advent_calendar_2013_14im-workflow/

もう五年も前の記事ですか……。
2013年ということは、『intra-mart Accel Platform』の初期には既に存在していたコマンドなのですね。

Tips
intra-mart Accel Platform(iAP)は、2012年秋からの呼称となります。
以降、春夏秋冬(後に秋がなくなる)のシーズンごとにバージョンアップする今のスタイルに変わりました。

もちろん、現時点での最新バージョン(2018Summer)でもこのコマンドは有効です。

いずれにせよ、intra-martのガイドラインを隅々まで読破していないと、あとあとまで気づけない可能性は大いに有り得そうです。

おわりに

intra-mart開発ブログいわく、今回ご紹介したようなコマンドは他にもあるようです。
ゲームの裏技を探すかのごとく、片っ端から調べてみるのも面白いかもしれません。
が、そういった調査に割ける時間もなかなかありませんし、ぱぱっと一覧にして見せてもらえたらうれしいナ、というのが正直なところですね。

それでは、今回はこれまで。
時節柄、体調も崩しやすいかと思われますので、みなさまくれぐれもご自愛くださいませ。

多要素認証を活用しよう

はじめに

こんにちは、前回に引き続きマサキです。

ようやく秋らしい気候になったかと思えば猛暑がぶり返し、台風が逆走したと思えば次はダブル台風の到来。
なんとも波乱に満ちた八月もまもなく終わりますね。みなさま、いかがお過ごしでしょうか。

今回は『多要素認証』についてお話ししたいと思います。
この記事については、インターネットに触れるすべての方に一読していただきたい内容となっています。

後半は弊社、株式会社グローバル・ソリューションズ(以下GSOL)の製品もご紹介していきますので、最後までどうぞお付き合いください。

認証とは

まず、認証とは何でしょうか。

何かのサービスを受けるときに、自分が正規のユーザーであること、または『私が本物の私であること』を証明することを認証と言います。そしてそのために使うのが、秘密の文字や番号、生体情報です。 最近どんどん身近になっていますね。

例を挙げてみましょう。

このように、今や誰しもが自分だけのパスワードを持ち、日常的に認証を行っています。

多要素認証とは

では、多要素認証とは何でしょうか。

例えば『パスワードによる認証』に加え、もう一つ『指紋による認証』など別の認証を続けて行う方式のことです。複数の認証を必要とするから『多要素』と呼ぶ、分かりやすいネーミングですね。

多要素のイメージ
多要素のイメージ

また、異なる認証を二つクリアして初めてログインできるような仕組みのことは、特に『2段階認証』と呼びます。
2段階認証のイメージ
2段階認証のイメージ

最近になって増えてきたとされる認証方式ですが、一部銀行などではもっと昔から採用されていました。
利用者の大切な資産を守ろうとする金融機関の、セキュリティに対する厳格な姿勢がうかがえますね。

認証方法が変わってきた契機

最近になって増えてきた、と上述しましたが、それはなぜでしょうか。どんな契機があって、新しい認証方法が登場したのでしょうか。
実は昨年(2017年11月)、総務省の『国民のための情報セキュリティサイト』にて、こんな発表があったのです。

利用するサービスによっては、パスワードを定期的に変更することを求められることもありますが、実際にパスワードを破られアカウントが乗っ取られたり、サービス側から流出した事実がなければ、パスワードを変更する必要はありません。

さらに、こう続きます。

むしろ定期的な変更をすることで、パスワードの作り方がパターン化し簡単なものになることや、使い回しをするようになることの方が問題となります。

このように、パスワードの定期変更はする必要がない、むしろリスクがある、と明記されています。

Tips
※下記サイトから引用しておりますので、全文はリンク先をご覧ください。
http://www.soumu.go.jp/main_sosiki/joho_tsusin/security/business/staff/01.html

驚いた方も多いのではないでしょうか。
ひと昔前は『パスワードは定期的に変更する』のが当たり前でした。
Webサービスなどでは、最後にパスワードを変更してから三カ月を経過したら、強制的にパスワード変更画面が表示される――ということもしばしばありました。

新しいパスワードを要求する画面イメージ
新しいパスワードを要求する画面イメージ

確かに、定期的にパスワードを考えるのも面倒なもので、つい『MGs1kIgs0l@0001』→『MGs1kIgs0l@0002』というような単純な変更にしてしまいがちです。
これが逆に危険ということで、定期的なパスワード変更を推奨しなくなった、ということですね。

日進月歩の情報分野では、今まで常識とされていたルールが目まぐるしく入れ替わる。これがITの難しいところであり、素晴らしいところでもあります。
パスワード認証についても、今回のことで大きな方針転換がなされたと言えるでしょう。

最適な認証

では、今日の『最適な認証』とはなんでしょうか?
最新のセキュリティ対策をまとめる『OWASP Top 10 - 2017』にはこのように記述されています。

かつてベストプラクティスとされてきたパスワードの定期変更や複雑性の要求は、ユーザーに弱いパスワードを繰り返し使うよう促すとの見方があります。そこで、あらゆる組織がNIST 800-63に従ってこのようなプラクティスをやめ、多要素認証を使うことが推奨されています。

Tips: NIST 800-63とは
アメリカの政府機関が、アメリカ政府各省庁・各機関に対して発行する電子認証ガイドライン

この記事の冒頭で触れた『多要素認証』が登場しました。
これまで使っていたパスワードの定期変更はやめて、多要素認証を使いましょう、という記述です。

Tips
全文は下記PDFをご覧ください。
https://www.owasp.org/images/2/23/OWASP_Top_10-2017%28ja%29.pdf

こういった流れの中で、日本でも多要素認証が広がりつつあるわけですね。

弊社GSOLの取り組み

NTTデータ・イントラマート社のパートナー会社である私たちGSOLでは、intra-mart製品として、2013年から『2段階認証&ログイン履歴』の仕組みを提供しています。

Tips: intra-martとは
『intra-mart Accel Platform』
NTTデータ・イントラマート社が販売している製品で、システム開発時に利用するアプリケーション構築基盤です。
詳しく知りたい方は、ぜひ検索してみてください。

intra-martで標準利用できるパスワード認証に加え、ワンタイムパスワード認証を使用することで、2段階認証を実現しています。

Tips: ワンタイムパスワード認証とは
一度のログインに一度きり使えるパスワードを通知して、それを入力することで認証をクリアする方式です。

ワンタイムパスワードはログインごとに別途通知されますので、万が一パスワードが盗まれていても、その通知を盗聴されなければ、なりすましログインの危険性は低くなります。

ワンタイムパスワードの通知方法は様々ありますが、弊社製品においては大きく二つあります。

  • スマートフォンアプリである『Google 認証システム』のコードを利用する
  • intra-martに登録したメールアドレスに都度送信する

どちらも原理は同じですが、『Google 認証システム』はスマートフォンAndroid / iPhone)を持っていないと利用できないデメリットがあります。

スマホアプリを利用しない人もいる
スマホアプリを利用しない人もいる

その点、メール送信なら、アドレスとネット環境があれば誰でも利用可能です。
このように、システム利用者の傾向を見つつ、最適な通知方法を選択することができます。

なお、『2段階認証&ログイン履歴』の設定は画面やAPIから可能です。
さらに、ノンコーディングシステム開発ができるIM-LogicDesignerとの連携することでも、2段階認証の設定が行えます。

Tips
詳しくは弊社のintra-mart ソリューション 製品情報サイトをご覧ください。
http://global-solutions.co.jp/documentation/index.html

まとめ

それでは、今回のまとめです。

  • 多要素認証を活用しよう!
  • GSOLの『2段階認証&ログイン履歴』をよろしく!

インターネット上のセキュリティ対策はイタチごっこです。今このとき最新最良のセキュリティも、十年もすれば時代遅れと言われていることでしょう。

十年後の技術
十年後の技術イメージ

インターネットと実生活が切っても切れないほど密接になった現代では、私たちのような情報技術者のみならず、すべてのネット利用者が、セキュリティを意識していかなくてはなりません。
今回の記事が、その意識向上の一助になればと願っています。

それでは、また次回もよろしくお願いいたします。

IM共通マスタの期間化を利用してみよう

ご挨拶

みなさま初めまして、マサキと申します。
理系出身のSE・プログラマでありながら、小説を書くのが好きだという変な技術者です。

今年から株式会社グローバル・ソリューション(以下GSOL)に中途入社致しまして、このたび本ブログの投稿を任せていただきました。

今回のテーマ

さて、今回の記事のテーマですが、intra-martが持つIM共通マスタの『期間化』についてご紹介していきます。
intra-martを導入したら、どんなことができるんだろう? とご検討中の方々の一助になれたらと思います。

Tips
intra-martってなんだろう? という方もまだまだ大勢いらっしゃるかと思われますが、気になった方はぜひ検索してみてください。
機会があれば、このブログでもあらためてご紹介するかもしれません。

マスタの期間化

1.IM共通マスタ

intra-martは、そのシステムを円滑に動かすため、『IM共通マスタ』という情報をデータベースに持っています。
マスタとは、ユーザ(社員)情報、会社・組織情報、品目(商品)情報など、システムを稼働させるにあたって欠かせない、基本的な情報のことです。

Tips
マスタについてもっとよく知りたいという方は、マスタデータやトランザクションデータをキーワードとして、調べてみてくださいね。

例えば、intra-martを使って構築した勤怠管理システムであれば、
ひとつの会社の中にいくつもの組織があり、さらにその組織それぞれに大勢の社員が所属している……という情報が、このIM共通マスタに保存されています。

マスタ情報のイメージ図
マスタ情報のイメージ図

ところで、会社ではしばしば組織の統廃合が行われます。となればもちろん、それにあわせてマスタも更新する必要があります。
廃止される組織の情報はどうすればよいでしょうか? 思い切って、データベース上からその組織の情報を物理削除してしまいましょうか?
ですがその場合、後から廃止された組織のことを調べようにも、データはもう存在しませんので、確認が難しくなってしまいます。

そんなときは、IM共通マスタが持つ『期間の分割』と『構成からの除外』という機能を活用しましょう。

2.期間の分割

IM共通マスタ上で、組織情報は期間情報を持っています。つまり、この組織は1970年1月1日から2099年12月31日まで有効ですよ、というような情報を保持しています。
期間の分割とは文字通り、その期間情報を二つに分けます。

組織分割のイメージ図
組織分割のイメージ図

『この組織は1970年1月1日から2018年7月31日で有効であり、さらに2018年8月1日から2099年12月31日まで有効である』というように、期間を分けてしまうのです。
このとき、前者を期間A、後者を期間Bとしましょう。

同じ組織でも、ある日を境に状況が変わったときなどに、この分割は役に立ちます。
この組織が2018年7月31日まで存在し、それ以降は廃止されたとするならば、期間Aをそのままにして、期間Bの情報のみ更新すればいいのです。

組織廃止のイメージ図
組織廃止のイメージ図

これなら廃止後も情報は残っていますので、将来必要になってから情報を取得することも可能です。便利ですね。

3.構成からの除外

組織を廃止する日付で期間を分割したら、廃止される期間の組織情報を修正しなくてはなりません。
通常、不必要な期間は『無効化(論理削除)』してしまえば大丈夫です。
これならば、あとから廃止組織の情報を調べることもできますし、さらに分割した別の期間で廃止組織を復活させることも容易です。組織に所属しているユーザもそのままですので、誰が所属していたかも保持しておけます。

ただし組織に関しては、『構成からの除外(論理削除)』をオススメします。
つまり、組織は有効な状態のままで、階層構造から外してしまうのです。

構成からの除外イメージ図
構成からの除外イメージ図

構成に残したまま無効化すると、組織マスタの操作画面上で組織名が赤字のまま残ってしまいます。

組織を無効化した場合
組織を無効化した場合

ですが構成から除外すれば、画面上からも組織名が消えますので、きれいに整理できますね。

組織を構成から除外した場合
組織を構成から除外した場合

構成から除外した組織は、一般ユーザの目には映らなくなります。
しかしデータとしては有効なままですので、そのまま別の組織の下部組織として配置換えするなどが可能となります。
また、退職した社員データをまとめておくための仮組織として、構成から除外した組織を利用するという手法も考えられます。

4.プログラムからの操作

期間についての操作は、IM共通マスタの各種画面から実行できますが、APIでも同じことができます。
例えば組織であれば、CompanyManagerというAPIを使います。
以下にメソッドを例示します。

メソッド名 役割 利用にあたり必要な情報
getDepartmentTerm 機関の検索 会社・組織検索条件、有効日付
separateTermDepartment 期間の分割 会社・組織検索条件、期間コード、分割日付
moveTermDepartment 期間の変更 会社・組織検索条件、期間情報

これらのメソッドを用いれば、マスタメンテナンス画面上でできるほとんどのことが、Javaプログラムでも実行できます。

Javaプログラムで操作できるということは、例えば休職申請ワークフローの案件終了時に自動で休職期間をマスタ登録したり、IM-FormaDesignerやTERASOLUNAを使って独自に作った画面上で期間の操作を行ったりもできます。
夢が広がりますね!

夢が広がっている図

Tips
APIの詳しい機能については、公式リファレンスをご覧ください。
https://www.intra-mart.jp/apidoc/iap/javadoc/im_master_apidocs/jp/co/intra_mart/foundation/master/company/CompanyManager.html

まとめ

それでは、今回のまとめです。

  • intra-martの持つマスタ情報は期間化できる!
  • 期間を分割して、過去の情報を保持できる!
  • 廃止組織は『構成から除外』しよう!

いかがでしたでしょうか?
今後は、intra-martの紹介や小ネタの共有、最新技術やITニュースにちょっと触れてみたり、弊社GSOLのお話もしていけたらと思います。

私もまだまだ勉強中の身ではございますが、これからもどうぞよろしくお願いいたします。

Final Tips
intra-martは日々進化しておりますので、今後のアップデートで仕様が変わる可能性があります。

今年も内定者の研修にProgateを使ってみた話

開発部の高橋です。
一年近く更新をさぼっていましたが、再開します。

去年に引き続き、今年も新入社員の研修(今回は内定者研修)にProgateを使ったので、 継続して利用したことで気づいたことや、弊社の新入社員への研修内容について共有したいと思います。

ちなみに1年前に導入した時の所感についての記事は以下になります。

gsol.hatenablog.com

Progateとは

prog-8.com

Progateの解説は数多存在するため詳細は割愛しますが、イラスト中心の直感的なスライド資料と実践的な演習問題の添削によって、初学者でも楽しくプログラミングの基本の「き」を学べるサービスです。

今回は内定者研修の教材として、以下のレッスンを3ヶ月かけて受講してもらいました。

Progateによる内定者研修

今年の内定者研修は1月から3月に実施しました。
研修では内定者の時間的都合を尊重して、特に弊社側で厳守すべきスケジュールを設けずに個人のペースでProgateのレッスンを進めてもらいました。
※とはいえ流石に基準となるスケジュールがないと予定通り進めるのも難しいので、毎週5時間程度の受講で上記の全コースが完了できる点と、レッスンを受ける際の適切な順番(フロントエンド→バックエンド→その他の順番)を内定者に伝えています。

去年はあまり効果を実感できず…

ちょっと余談ですが、去年Progateを利用した時は情報系出身のとても優秀な学生が受講者だったこともあり、Progateの効果がいまいち把握できませんでした…
優秀な学生が入社してくれることはとても嬉しいのですが、Progateの全レッスンをすぐに完了させてくれる等、中々に研修担当者泣かせの子でした。

今年は効果を実感できた!

今年は内定者の子にプログラミング未経験の文系の学生がいて、Progateを通じて初学者からエンジニア見習いレベルまで成長していることを確認できたので、良い効果があることを実感できました。
内定者の子には3月からアルバイトとして本社で働いてもらっているのですが、先輩社員と技術的な会話が成立する程度には成長できています。
新卒採用の一次面接の時点で、「プログラミング未経験ですが大丈夫ですか?」といった質問をしていたことを思い出すと、今年もProgate使ってよかったなあとしみじみと感じます。

Progateのどの辺りでスキルを付けることができたかを内定者の子に聞いてみたところ、
コースの後半で難易度が飛躍的に上がる箇所があり、そこを自力で調べて解決することでスキルを付けることができたという感想を聞くことができました。
技術的な問題を如何に調べるかがエンジニアにとって最も重要なスキルなので、そのスキルを初学者の時点から身に着けられるサービスであるという点が、Progateがここまで支持されている理由なのかなと感じました。

仲間募集中!

グローバル・ソリューションズでは、新卒採用で仲間を募集しています。弊社は初学者の方に成長できる環境、様々なことを経験、チャレンジできる環境を提供できます。興味のある方は、以下から説明会にエントリーしてみてください。

job.rikunabi.com

またWantedlyでは、新サービスの開発コアメンバーとして大いに力を発揮してくれる方を募集しています(昨日募集を開始しました)。新卒中途は特に問いませんので、弊社のサービス開発に少しでも興味のある方は以下からエントリーお願いします!

www.wantedly.com

新入社員の研修にProgateを使ってみた話

開発部の高橋です。

4月からの新入社員研修にProgateというサービスを使ってみました。Webサービスを利用した新入社員研修は初めての試みだったので、私個人の所感も交えて紹介しようと思います。

Progateとは

prog-8.com

初学者向けのオンラインプログラミング学習サービスです。以前から新入社員研修で使ってみたいと思っていたので、今年度から試験的に導入してみました。

f:id:gsol-dev:20170501101523p:plain

学習資料のスライドが初学者向けにとてもわかりやすく記載されてます。画像はProgateトップページより引用しています。

f:id:gsol-dev:20170501101529p:plain

学習資料に対応した演習をこなしながら学習を進めていく形式となっています。この画像もProgateトップページからの引用です。

実際に利用してみた感想

以下、私個人の所感です。

初めてのプログラミング向き

学習スライドが読みやすいように図が多め、文章少なめだったり、演習にヒントがついていたりと、とにかくプログラミング初心者に優しいサービスの作りになっている印象です。本を買って勉強する場合と比べて、かなり勉強しやすいと思います。

場合によっては他の研修と併用が必要

言語によっては学習範囲が狭いため、他の教材(本とか)での学習が必要になると思います。例えば自社の場合、クライアントサイドの研修に関してはProgateのHTML&CSSjQueryで新人の研修教材として十分でしたが、サーバー言語のJavaJavaScriptについてはProgateの内容では不十分そうだったので、Progate学習後に本を読んで勉強をしてもらっています。

ちなみに自社の開発にRailsを使っている場合は、HTML&CSS + jQuery + Ruby on Railsで新人研修としては十分な印象です。

まとめ

以上、Progateの紹介と新入社員研修に使用した感想でした。最初にプログラムに触る教材としてはとても良いサービスなので、来年度も新入社員研修に使ってみようと思います。

CentOS Dockerコンテナの日本語ロケール設定方法

開発部の高橋です。

今回はCentOSのDockerコンテナで日本語ロケール設定方法について紹介します。

CentOSでのロケール設定方法

以下Dockerfileのサンプルです。

FROM centos:centos7

RUN rm -f /etc/rpm/macros.image-language-conf && \
    sed -i '/^override_install_langs=/d' /etc/yum.conf && \
    yum -y reinstall glibc-common && \
    yum clean all

ENV LANG="ja_JP.UTF-8" \
    LANGUAGE="ja_JP:ja" \
    LC_ALL="ja_JP.UTF-8"

CMD bash

補足

これまでは以下の様なDockerfileでもロケールの設定ができていたのですが、Bug 1392855 – build-locale-archive: build-locale-archive.c:301: fill_archive: Assertion `used < head->namehash_used' failed.で報告されている不具合によって、去年末頃からロケールの設定ができなくなっています。

FROM centos:centos7

RUN yum -y update && \
    yum -y reinstall glibc-common && \
    yum clean all

ENV LANG="ja_JP.UTF-8" \
    LANGUAGE="ja_JP:ja" \
    LC_ALL="ja_JP.UTF-8"

CMD bash
# コンテナ内でlocale -aを実行してもCロケールとPOSIXロケール以外が存在しない。コマンド実行する度にwarningログが出力される。
$ locale -a
/bin/sh: warning: setlocale: LC_ALL: cannot change locale (ja_JP.UTF-8)
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_COLLATE to default locale: No such file or directory
C
POSIX

そのため、glibc-commonを再インストールする前にrm -f /etc/rpm/macros.image-language-confsed -i '/^override_install_langs=/d' /etc/yum.confを実行することで不具合を回避しています。

intra-martのエラー発生時にSlackに投稿する方法の紹介

開発部の高橋です。

今回はlogbackの機能を使って、intra-martでエラーが発生した時にSlackにエラー内容を投稿する方法を紹介します。

Slackの設定

SlackのIncoming Webhooks機能を使って、 特定のチャンネルにintra-martのエラー内容を投稿するためのWebhook URLを発行します。 (手順の紹介は省きます)

自社ではエラーが発生すると、:imp:の絵文字で投稿するように設定しています。

f:id:gsol-dev:20170123201536p:plain

Appenderの実装

Appenderによって、エラーログをSlack APIの仕様に基づいたJSON形式に変換し、Webhook URLに対してPOST送信します。 長文のエラーログをSlackに投稿できるように、attachments属性にエラーログを設定しています。

以下Appenderのソースです。

package jp.co.gsol.logback.appender;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Layout;
import ch.qos.logback.core.UnsynchronizedAppenderBase;

import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.arnx.jsonic.JSON;

public class SlackAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

    private static final String PRETEXT = "【社内システム】iAPエラーログ";
    private String url;
    private String channel;
    private Layout<ILoggingEvent> layout;
    private int timeout = 10000;

    @Override
    protected void append(final ILoggingEvent event) {
        try {
            final URL apiUrl = new URL(url);
            final StringWriter w = new StringWriter();
            w.append("payload=").append(URLEncoder.encode(createAttachmentsStr(layout.doLayout(event)), "UTF-8"));
            final byte[] bytes = w.toString().getBytes("UTF-8");

            final HttpURLConnection con = (HttpURLConnection) apiUrl.openConnection();
            con.setRequestMethod("POST");
            con.setDoOutput(true);
            con.setConnectTimeout(timeout);
            con.setReadTimeout(timeout);
            con.setFixedLengthStreamingMode(bytes.length);
            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

            final OutputStream os = con.getOutputStream();
            os.write(bytes);

            os.flush();
            os.close();
        } catch (IOException e) {
            addError(channel + "投稿時にエラーが発生しました", e);
        }
    }

    /**
     * ログフォーマットをJSON形式に変換
     * @param text
     * @return
     */
    private String createAttachmentsStr(final String text) {
        List<Map<String, Object>> attachments = new ArrayList<>();
        Map<String, Object> attachment = new HashMap<>();
        attachment.put("pretext", PRETEXT);
        attachment.put("text", text);
        attachments.add(attachment);
        Map<String, Object> map = new HashMap<>();
        map.put("attachments", attachments);
        return JSON.encode(map);
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(final String url) {
        this.url = url;
    }

    public String getChannel() {
        return channel;
    }

    public void setChannel(final String channel) {
        this.channel = channel;
    }

    public Layout<ILoggingEvent> getLayout() {
        return layout;
    }

    public void setLayout(final Layout<ILoggingEvent> layout) {
        this.layout = layout;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }
}

im_logger.xmlの設定

im_logger.xmlにSlackAppenderの設定を行います。 intra-martのログ仕様書に基づいて、 ログ出力したい情報を<layout>タグに設定します。<url>タグにはSlackのWebhook URLを設定します。

以下、自社のim_logger.xmlの設定の抜粋です。

    <appender name="SLACK" class="jp.co.gsol.logback.appender.SlackAppender">
        <channel>#in-house-error-log</channel>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}]
thread: [%thread]
level: [%-5level] logger: [%logger{255}]
tenant id: [%X{tenant.id}] log id: [%X{log.id}] request id: [%X{request.id}]
user type: [%X{user.type}] authenticated: [%X{authenticated}] user cd: [%X{user.cd}]
log message code: [%X{log.message.code}]    message: %msg%n
%ex{full}
            </pattern>
        </layout>
        <url>https://hooks.slack.com/services/XXXXXXX/YYYYYYY/ZZZZZZZZZZZZZZZZZZZZZ</url>
    </appender>

    <appender name="ASYNC_SLACK" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="SLACK" />
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>

    <root>
        <level value="info" />
        <appender-ref ref="ASYNC_SLACK" />
    </root>

Slackに投稿される内容

intra-martでエラーが発生すると、Slackに↓の様な投稿が自動で行われます。

f:id:gsol-dev:20170126195917p:plain

投稿を展開するとExceptionの詳細が見れます。

f:id:gsol-dev:20170126195941p:plain

Exceptionが長すぎると、途中で省略されてしまうので、いつか修正したいです。

以上、intra-martのエラー発生時にSlackに投稿する方法の紹介でした。

JavaScriptを使った開発に便利な定番ライブラリ

開発部の佐野です。

JavaScriptの定番ライブラリについて紹介します。

lodash.js

非常に幅広く便利な機能を提供してくれるライブラリです。 主にコレクションやオブジェクトに関しての機能が中心で、かゆいところに手が届くようなユーティリティをたくさん備えています。

Moment.js

日付や時刻に関する機能を提供するライブラリです。 JavaScript標準の機能だと、日付の操作に不便なことが多々あります。 このライブラリは、そんな日付関係の処理を補ってくれるため、非常に便利です。

この2つで、細かな使い勝手がかなり改善されます。 画面構築と操作に関しては、jQueryやAngular.js、最近ではReact.jsなど様々な選択肢がありますが、この2つのライブラリはどの場合でも役に立つと思います。

当然使い方を覚える必要はありますが、それを補って余りあるメリットがあると思うので、ぜひ活用してみてください。

docker-compose runでport mappingされず小一時間悩む

開発部の高橋です。

docker-composeを使っていたらタイトル通りの内容で悩みました。結論としてはドキュメントをよく読んで使おうって話なだけなのですが。。。

経緯

以下の様なdocker-compose.ymlを作って、dockerコンテナ上でNode.jsプロジェクトのインストール、起動すると、コンテナ内で起動しているサーバーにアクセスできずに悩みました。。。

# docker-compose.yml
version: '2'
services:
  graphql:
    image: node:alpine
    volumes:
      - .:/usr/src
    ports:
      - "3000:3000"
    privileged: true
# Node.jsプロジェクトをcheckout
git clone https://github.com/kadirahq/graphql-blog-schema.git
cd graphql-blog-schema
git checkout build-schema

# 作成したdocker-composeでインストール、起動
docker-compose run --rm graphql npm install --prefix=/usr/src
docker-compose run --rm graphql npm start --prefix=/usr/src

# 別ターミナルでコンテナ内のサーバーにアクセスするとConnection refused
curl http://localhost:3000
curl: (7) Failed to connect to localhost port 3000: Connection refused

原因と解決

調べてみると(というかリファレンスに書いてありました)、docker-compose run --service-portsとしないと、docker-compose.ymlファイルのport mappingが無視されるようです。以下引用です。

The second difference is the docker-compose run command does not create any of the ports specified in the service configuration. This prevents the port collisions with already open ports. If you do want the service’s ports created and mapped to the host, specify the --service-ports flag:

docker-compose run --service-portsを指定して無事動きました。

docker-compose run --rm --service-ports graphql npm start --prefix=/usr/src

# curlでコンテナ内のサーバーにアクセスできる
curl http://localhost:3000
<html>
  <head>
    <title>Sample App</title>
    <link rel="stylesheet" type="text/css" href="/static/graphiql.css">
  </head>
  <body>
    <div id='root'>
    </div>
    <script src="/static/bundle.js"></script>
  </body>
</html>

以上、ドキュメントをよく読んで使おうという話でした。

JavaScriptのアロー関数について

開発部の佐野です。

JavaScriptのECMAScript6で追加されたアロー関数を紹介します。

これは、要は関数を定義する構文です。 従来のfunctionと書く定義とは少し挙動が違います。 それに加えて、より短く簡単に関数を定義できるというのもメリットになります。

ただ、このアロー関数は括弧を省略できたりして、意外と覚えにくい機能です。 特にES6の新しい構文に慣れていない人は混乱しがちです。

例えば、以下の書き方は全て同じ結果になります。

name => 'Hello ' + name;
name => { return 'Hello ' + name; };
(name) => 'Hello ' + name;
(name) => { return 'Hello ' + name; };

左右どちらの括弧も省略できるため、結果的に4パターンの書き方があります。 引数が複数の場合や、処理が複数行の場合は省略できません。

アロー関数はES6の構文で書く場合は必須となる構文なので、覚えると将来役に立つでしょう。 また、ES6で書かれたJavaScriptが読めない人の原因は、大抵このアロー関数が理解できない場合が多い気がします。 逆にこれさえ分かれば、敬遠しがちだった最新のJavaScriptソースコードもグッと理解しやすくなると思います。

以上、アロー関数の紹介でした。

JavaScriptのテンプレート文字列について

開発部の佐野です。

今回は、JavaScriptのテンプレート文字列について紹介します。

これはECMAScript6で組み込まれた新しい機能です。 他にも様々な機能が追加されていますが、テンプレート文字列は汎用性が高く、様々な場面で使える機能です。

「`」(バッククォート)で囲うと利用できます。 内部に変数などを埋め込んで表示できるのが特徴です。

例えば、以前は文字列の途中で変数を入れるにはこのように書いていました。

var name = 'GSOL';
console.log('Hello ' + name + ' !!');

これをテンプレート文字列にすると、

var name = 'GSOL';
console.log(`Hello ${name} !!`);

と書けます。

地味ですが、きれいに書けるので開発者的には嬉しい機能です。

以上、簡単ですがテンプレート文字列の紹介でした。

markdownエディタの紹介

開発部の佐野です。

今回は、markdownエディタについて紹介します。

markdownは書きやすくで便利なので、よく使っています。 しかしローカル上のmarkdownファイルを編集するエディタは、意外と定番のものが無い気がします。 なので個人的に使ってみたmarkdownエディタを紹介してみます。

Haroopad

非常にシンプルで使いやすいと思います。 メモ帳にmarkdownのプレビュー画面が追加されたような感じです。 後述するAtomなどは多機能でタブなども使えますが、もっとシンプルに使うならこちらのほうが良いと思います。 すこしフォントが微妙ですが、設定で変えることができます。

Atom

markdown専用のエディタではなく、様々なプログラミング言語の編集に使えるエディタです。 多機能でカスタマイズも柔軟にでき、様々な機能の追加パッケージが公開されています。

Typora

markdownかつWYSIWYGというユニークなエディタです。 markdownの記法を書くと、自動的に表示が更新されていきます。 普段直接markdownを書くとテーブルなどが少し書きづらいのですが、Typoraはテーブルを作ると専用の操作メニューが表示されて、非常に簡単に編集ができるようになっています。 いきなり表示が更新されることなどから、若干操作しにくい感じはありますが、おもしろいエディタだと思います。

markdownエディタの紹介は以上です。

markdownはシンプルで覚えやすく、日常のメモ書きから開発資料まで幅広く役立ちます。 このブログも含めてmarkdownで書けるサービスも数多くありますので、試してみてはいかがでしょうか。

自社githubアカウントの紹介

開発部の高橋です。

自社githubアカウントについて簡単に紹介しようと思います。

自社githubアカウント

github.com

github上で公開しているリポジトリ

自社intra-martソリューション関係

IMBoxデスクトップ通知、iACスケジュール連携ソリューションのユーティリティやプラグインgithub上に公開しています。

PeerJS

PeerJSからForkして一部修正を加えてます。 Web会議ソリューションのSignalingサーバー使用しています。

Dockerfile

社内の開発等に使用しているDockerfileを公開しています。 Docker Hubでimageも公開してます。自社Docker Hubのページ

以上、自社github(とDocker Hub)アカウントの簡単な紹介でした。

react.jsの入門書を購入しました

開発部の高橋です。

以前から探していたReactの入門書が先日発売したので、 会社の経費で1冊購入しました。 (お願いしたら二つ返事で購入してくれる会社に感謝です)

WebデベロッパーのためのReact開発入門 JavaScript UIライブラリの基本と活用

WebデベロッパーのためのReact開発入門 JavaScript UIライブラリの基本と活用

Reactって何?って開発メンバーもまだいるような状況なので、 この本でReactの基本を固めてサービス開発を進めていきたいと思います。

現在WantedlyでReactを使ってサービスの開発したい方を募集してますので、 興味がある方はぜひ応募してみてください。 上で書いたように社内は現在React勉強中なので、 Reactに興味があって少し触ったことのあるレベルの方でも大歓迎です。

www.wantedly.com

HTML5の新機能紹介 ~WebStorage~

開発部の真崎です。

今回は、HTML5で導入された新機能の1つ、WebStorageについての紹介です。

WebStorageとは

WebStorageとは、HTML5で導入されたブラウザでデータの永続化を行う仕組みのことです。 特徴としては、 * 比較的大容量の文字列データを保存できる * Key-Value型のシンプルなインターフェイス * 保存/取得処理に通信を伴わない などがあります。 今までは、毎回サーバにアクセスし、保存/取得処理されていたブラウザ固有のデータをブラウザ内で完結させることができます。 また、最近使われている大多数のブラウザで使用できる(ChromeFireFoxSafariOpera、IE8~)ので、安心して使えます。

Cookieとの違い

WebStorageと似た技術で、今まではCookieがありました。違いについて、簡単にまとめてみました。 (localStorageとsessionStorageの違いは、後で説明しています)

WebStorage Cookie
Origin毎の保存容量 5MB (仕様上) 4KB
有効期限 なし (localStorage) あり
通信 jsで実装する必要がある 勝手に送信される
個数制限 なし 20個 (仕様上)
pathの設定 Originで共通 できる
標準API 比較的簡単 煩雑な文字列操作
ウィンドウ間でのデータの共有 できる (localStorage) できる
スコープ Origin単位 host/path単位

有効期限を設定したい場合や常にデータを送信したい場合、特定のpath以下のみのアクセスを許可したい場合など、 Cookieでしかできないこともありますが、データ保存に関しては、WebStorageに利点が多いです。

WebStorageの種類

WebStorageには、用途に応じて2種類のストレージが用意されています。違いについて、簡単にまとめてみました。

locakStorage sessionStorage
有効期限 なし(削除されるまで) ウィンドウ(タブ)を閉じるまで
ウィンドウ(タブ)間での
データの共有
できる できない

基本的な機能については、共通です。1点だけ異なっており、
localStorageは、Origin(scheme://hostname:port/)にスコープしており、
sessionStorageは、更にTop-level browsing context(ウィンドウ、タブなど)にスコープした Storageである点が差異です。(※sessionStorageは、Webアプリなどのログインセッションとは同期してないです)

用途としては、localStorageはデータの永続化、sessionStorageは一時データの保存になると思います。

WebStorageの使い方

コードを示しながら、説明します。 (localStorageもsessionStorageも同じインターフェイスを備えてます。ここでは、sessionStorageをサンプルとします。)

// sessionStorageが使えるかどうか
if (typeof sessionStorage === 'undefined')
    alert('sessionStorage is not found');

// 文字列データの操作は、hashを操作するように行えます。
sessionStorage['key'] = 'value';
console.log(sessionStorage['key']); // => 'value'
// 次の書き方もできます
//sessionStorage.setItem('key', 'value');
//console.log(sessionStorage.getItem('key'));
//sessionStorage.key = 'value';
//console.log(sessionStorage.key);

// Objectの保存には、JSON.stringifyなどで、シリアライズする必要があります。
sessionStorage['objKey'] = JSON.stringify({key: [1, 2, 3]});
// 取得時には、デシリアライズしてください。
console.log(JSON.parse(sessionStorage['objKey'])); // => {key: [1, 2, 3]}

// データを消したいとき、次のようにできます。
//sessionStorage.clear(); // 全件削除(Originに紐づく全件が削除されるので注意)
sessionStorage.removeItem('key'); // 'key'で登録されているデータが削除されます
// OriginでStorageが共有されているので、次のようにnamespaceを分けると削除しやすいです。
sessionStorage['prefix.key'] = 'value'; // 一意なprefixを付加
Object.keys(sessionStorage)  // -> ['objKey', 'prefix.key']
      .filter(function(k) { // prefixが一致するデータをfilter
          return k.split('.')[0] === 'prefix';
      }).each(function(k) { // filterされたデータを削除
          sessionStorage.removeItem(k);
      });

// その他のStorage interface
// n番目のkeyを戻します
sessionStorage.key(0); // -> 'objKey'
// Storageに保存されている件数(正確には、Storage自体のinterfaceではないですが)
sessionStorage.length; // -> 1
// 登録されているkeyの一覧
Object.keys(sessionStorage); // -> ['objKey']

// 他のタブからStorageの変更があった場合、eventが発火します
// (sessionStorageは仕様上、発火しないので、localStorageをサンプルにします)
localStorage['key'] = 'old';
// listenerを設定
window.addEventListener('storage', function(e) {
    console.log(e);
});
// 他のタブから
// localStorage['key'] = 'new';
// 登録、更新、削除など、Storageに変更があれば、eventが発火します
// => key: 'key', newValue: 'new', oldValue: 'old', ...

※Storageのデータは、Originで共有されるので、複数の機能で使用する場合、削除や上書きに気をつけてください。

使い分け

Cookie、localStorage、sessionStorageの違いを簡単に説明してきました。ここでは、それぞれの用途について考察してみたいと思います。

Cookie

容量が少なく、常にサーバとデータを共有しているCookieはログインセッションの管理が主な用途になると思います。

localStorage

ブラウザ毎にデータを永続的に保存できるので、設定の保存目的や、大容量なのを活かし、下書き保存などに使えると思います。 また、Storage変更時に、eventが発火するので、タブ間の同期などにも使えます。

sessionStorage

タブ毎にスコープが分かれているので、タブで行った作業結果の一時保存や、タブを閉じたあと必要がない閲覧情報などの保存に使えると思います。

まとめ

HTML5で導入されたWebStorageについて、Cookieとの比較を行い説明しました。 また、使い方について、サンプルコードを元に説明を行いました。 サーバに送らなくてもいいデータなどの保存に使用すると、画面遷移の高速化が期待できます。 サーバとの無駄な通信量が多い時などいかがでしょうか?