2019年12月9日月曜日

PHP Conference 2019に行ってきました。


オフィス狛 技術部のmmm(むー)です。
先週のことですが、PHP Conference Japan 2019に行ってきました。
(現在の業務ではPHPは使用しておりませんが…)

・ 公式URL
https://phpcon.php.gr.jp/2019/
・ セッションのタイムテーブル
https://fortee.jp/phpcon-2019/timetable

今年のテーマは 「beyond .*」で、これから先のPHPの未来がさらに飛躍するようにとつけたようです。
今回第20回目を記念して、PHPの像のぬいぐるみが販売されていました。

自由すぎると定評のPHPですが、先月末7.4がリリースされました。
オープイングセッションでは、PHP7.4の新機能の紹介がありまして、型付けプロパティやアロー関数を使用できるようになりました。
次にリリースされるバージョンは8になるようです。
また、5.6も7.1もEOL(End of Life)になったねって話が出てました。あっという間ですね…

カンファレンスに参加する醍醐味は、自分が知らない技術・考え・作者の方の思いを知ることができることだと思います。以下、各セッションの感想になります。

MVCにおける「モデル」とはなにか

天重誠二さん
https://speakerdeck.com/tenjuu99/what-mvc-is

フレームワークの話をするのかと思っていましたが、概念的な話でした。
ドメインモデルについての話を現実世界で使用している伝票システムにたとえて説明をしていたのが面白かったです。
また、誰にとってのデザインかを考えた上で設計するのは大切ですね。
今回の発表でたくさんの参考文献が紹介されていたのですが、その中の一つ「誰のためのデザイン」は私も以前プログラマーでも読んだ方が良いとデザイナーの方に勧められて読みました。
タイトルの通り誰のためにデザイン(設計)するのかということが、実生活を例に紹介されていてとても面白い本でした。他の参考文献も読んでみたいと思います。

思想と理想の果てに -- クリーンアーキテクチャのWEBフレームワークを作ろう

成瀬允宣さん
https://nrslib.com/phpcon-2019-proposal/

クリーンアーキテクチャとはフレームワークに依存しないという前提のもと、環境に依存しないフレームワークを作る説明のセッション。クリーンアーキテクチャを採用すれば、どの処理をそこに書いたか悩むことから解放されます。
この発表を他の会場で行った時、似たようなライブラリがあると参加者の方に聞けたので、アウトプットする人のところに情報が集まってくる(アウトプットしよう)とのことでした。
また、フレームワークは変わらないと思っていても、10年15年やってるとフレームワーク変えることはあり、10年後保守してる人達が辛い思いをするからフレームワーク非依存をおすすめしますといった内容でした。

PHPは何を捨て、どんな力を手に入れてきたのか

うさみけんたさん
https://www.pixiv.net/fanbox/creator/105589/post/691574

PHPを使用していると、PHP5.3を最後にPHP6がスキップされて、次は7になっていることは一目瞭然だと思います。
PHP5.3は、6が開発されるまでの繋ぎで、6でUnicode化等を行うつもりでしたが 開発が止まってしまったのでした…。
現在、他のモダンと言われている言語に追いつくためにPHPは歴史的経緯で残っているレガシーコードを消していこうという動きがあります。
は使用できないよう、PHPから派生したP++を作る…等、今年の夏頃議論が白熱してPHP界隈で炎上していました。
銀河の平和はまだわかりませんが、詳細は調べてみてくださいとのことでした。

PHPUnit: Past, Present and Future

Sebastian Bergmannさん
https://thephp.cc/dates/2019/12/php-conference-japan/phpunit-past-present-future

PHP Unitの開発者の方のセッションです。別のセッションの関係で、途中から聞きました。
Sebastianさんの誕生やプログラミングを始めたこと、PHPが誕生したこと、PHP Unitの開発を始めたこと順を追って各歴史を順番に振り返えるセッションでした。 PHP4では例外処理がなかったため大変だった等の話を聞いて、私にとってはあって当たり前の機能だったので、その時代の話が聞けて面白かったです。
セッション終了後には、エモかったという感想がTwitterにあふれていました。
ちなみに、PHP Unitのロゴは好きなゲーム、宇野デザイナーさんにお願いしたそうです。
英語でしたが、ゆっくり話してくださっていたので聞きやすかったです。

How to Supercharge your PHP Web API

Aurimas Niekisさん
※スライドがアップされていませんでした

私のプログラマーとしてのスキル能力と英語力が足りなくて、内容がわかりませんでした…
セッションが終了した後に、少し対面で話をさせていただいたのですが、Aurimasさんは来年の2月から日本で働き始めるようです。
また、私がNode.jsを今業務で使用していることを伝えると、Node.jsも使っているので質問があればいつでも連絡してねと言ってくださいました。(優しい…)


カンファレンスに参加していると知り合いが少しずつ増えていくので、時々集まる同窓会のようで楽しいです。
来年のPHP Conferenceは2020/10/11に開催されるようですので、都合が合えば参加したいです。


,

2019年11月30日土曜日

knex.jsで実行したSQL文を確認する。

オフィス狛 技術部のHammarです。

以前の投稿で、knex.jsのtipsをいくつか書いたりNode.jsでknex.jsを使ったSQL書き方Tips、弊社技術部のmmm(むー)が書いたりしていますが(knex.jsでfromに2つ以上のサブクエリを書く方法)、今回はもう1つ「実行したSQL文を確認する」やり方を書きたいと思います。

これ意外とよく使うのに、やり方がぱっとわからなくて、社内でもどうやるのか質問があったりしたので新たにtipsとして追加したいと思います。

もちろん公式サイトにも載ってはいるのですが、日本語対応されていないのでちょっとわかりずらいんですよね。
で、やり方は単純で、SQL実行文に.toSQL()という記述を追加することで確認可能です。

具体的には

■記述例1

knex
  .select('*').from('users')
  .where('id = ?', [1]))
  .toSQL()

上記の結果を出力すると、
bindings: [1],
method: 'select',
sql: 'select * from "users" where id = ?',
options: undefined,
のように出力されて、バインド値と実際のSQL文を確認することができます。

ちなみにpostgresqlはSQL実行文に.toSQL().toNative()と記述することで同様の結果が得られるようです。

■記述例2

knex
  .select('*').from('users')
  .where(knex.raw('id = ?', [1]))
  .toSQL().toNative()

出力結果
bindings: [1],
sql: 'select * from "users" where id = $1',

自分も最初はこのやり方を知らずに、デバッグでステップ実行してどこでSQL作っているのかずーっと奥の方まで追って行って、確認するのに結構苦労してました(苦笑)

以上のように知ってしまえば簡単なんですが、知らないとかなりやりずらいので、ご参考にしてみてください。

,

SpringBoot「1.5」を「2.1」へバージョンアップ。


オフィス狛 技術部のJoeです。

SpringBoot 1系が2019/8/1にEOLになり、担当しているプロジェクトもバージョンアップ要件(1.5 → 2.1)がありました。
バージョンアップで対応した内容の一部をご紹介します。

① application.ymlファイルのエラー

非推奨となったプロパティが大量に出てきました。こちらは全てメッセージに従いキー名を変更することで解決しました。

【参考)メッセージ】
Property 'spring.thymeleaf.content-type' is Deprecated: Use 'spring.thymeleaf.servlet.content-type' instead.
Property 'server.session.timeout' is Deprecated: Use 'server.servlet.session.timeout' instead.
Property 'spring.messages.cache-seconds' is Deprecated: Use 'spring.messages.cache-duration' instead.
Property 'spring.http.multipart.max-file-size' is Deprecated: Use 'spring.servlet.multipart.max-file-size' instead.
Property 'spring.http.multipart.max-request-size' is Deprecated: Use 'spring.servlet.multipart.max-request-size' instead.
Property 'spring.datasource.initialize' is Deprecated: Use 'spring.datasource.initialization-mode' instead.

② Beanオーバーライドが無効

次に下記エラーが発生しました。
The bean 'XXX', defined in class path resource [XXX.class], could not be registered. A bean with that name has already been defined in class path resource [XXX.class] and overriding is disabled.

SpringBoot 2系では、Beanオーバーライドがデフォルトで無効になったようです。
メッセージのActionに従い、application.ymlファイルに下記を追記しました。
spring.main.allow-bean-definition-overriding: true

③ 非推奨、廃止、構成変更のパッケージ

非推奨、廃止、構成変更になったパッケージがいくつかありましたので、下記のように修正しました。

■非推奨
【変更前】
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
【変更後】
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;

■廃止
【変更前】
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
public class MessageConfig extends WebMvcConfigurerAdapter { ・・・
【変更後】
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class MessageConfig implements WebMvcConfigurer { ・・・

■構成変更
【変更前】
import org.springframework.boot.web.support.SpringBootServletInitializer;
【変更後】
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

④ application.ymlファイルから@ConfigurationPropertiesで値が設定されない

こちらは動作確認をして気が付いたのですが、@ConfigurationPropertiesアノテーションを付けたクラスでsetterメソッドがstaticだと、application.ymlファイルから値を設定できなくなってしまいました。
下記のように非staticなメソッドに変更することで無事設定されました。
@Component
@ConfigurationProperties(prefix = "outerconf")
public class OuterConf {

  private static String rootdir;

  public static String getRootdir() {
    return rootdir;
  }

  // 非staticに変更
  public void setRootdir(String rootdir) {
    OuterConf.rootdir = rootdir;
  }
}


SpringBootのバージョンアップはたくさんの方が記事にされていますが、環境によって対応が異なるとは思いますので、何かお役に立てれば幸いです。

,

2019年11月29日金曜日

「コンテンツに応じた塗りつぶし」で写真の不要なものを消す。


こんにちは、オフィス狛 デザイン部のSatoです。

今回はPhotoshopCC2019から機能が強化された「コンテンツに応じた塗りつぶし」という機能を使って写真から不要なものを消す方法をご紹介しようと思います。
この機能を使うとあのAdobe Senseiが数分で遠目で見る分には問題ないくらいに自然に選択した部分の中身を近くの部分そっくりにしてくれます。

https://unsplash.com/photos/Osd4ngHD4kM
↑今回はこの写真から中央に立っている人物を消してみます。
とっても綺麗な写真ですが今は無人の廃墟の写真が欲しいので女の子にはどいてもらいましょう……。


まず、消したいものを選択していきます。
精密に消したい方は自動選択ツールなどがいいかと思いますが、今回はなげなわツールでざっくり選択しました。


選択後、Photoshopメニューバーの「編集」の「コンテンツに応じた塗りつぶし」をクリックします。


コンテンツに応じた塗りつぶしツールのワークスペースが表示されます。
左が編集画面、右がプレビュー画面です。


緑になっている部分はサンプリングされる(人工知能が参考にする)部分です。
右上のサンプリングブラシツールを使って緑の部分を微調整していきます。
サンプリング箇所の拡張と削除のブラシの切り替えは上部のサンプリングブラシツールのオプションの+と-のアイコンでも切り替えできます。またOptionかAltキーを押しっぱなしにしても切り替えができます。

消したい選択範囲を広げたい場合なげわツールで追加しましょう。


プレビュー画面で確認しつつ、いい感じになったら右下にあるOKボタンを押すと通常のワークスペース画面に戻り、(右にある出力先設定の値が初期設定の「新規レイヤー」になっている場合)修正した部分の新しいレイヤーができています。


違和感があまりなく人物が消えましたね!
しかし拡大しよく見ると、木と石が合体したような瓦礫などができあがってしまっていたり少し違和感があります。
拡大すると意外と気になりますね…。
このような気になる部分はコピースタンプツールで微調整をします。
全体的に見ると少し違いが分かりづらいので比較画像にまとめてみました。

この方法で修正した後の画像がこちらです。
遠目だと分かりずらいですが、比較画像の通り変になっている部分と人物の影をコピースタンプツールで消しました。
拡大しても大丈夫なくらいに違和感が減りました!
「コンテンツに応じた塗りつぶし」のみで完璧なものができる訳ではなさそうですが、コピースタンプツールやパッチツールで修正するよりもかなり手早く「遠目で見ると違和感ないレベルまで」修正できて感激です。

他の空の写真と組み合わせて夕暮れの写真を作ってみました。
この写真の空の部分のみトリミングし使いました。
色々と細かい調整が大変で人物を消す+微調整の10倍以上の時間がかかってしまいました。
つまり「コンテンツに応じた塗りつぶし機能」は本当に素早く人物を消すことができます!
コピースタンプツールで人や邪魔なものをチマチマ……消さなくていいのは本当にありがたいですね……。

Adobeのマニュアルに「コンテンツに応じた塗りつぶし」機能の詳しい使い方が書いてあるのでそちらもチェックしてみてください。


今回のブログ記事に使用した写真は全てUnsplashというフォトストックサイトのものを使わせていただきました。
Unsplashに掲載されている写真は無料かつクレジット不要で写真加工や商業利用可なのでデザイナー的に非常にありがたいサイトです。
More precisely, Unsplash grants you an irrevocable, nonexclusive, worldwide copyright license to
download, copy, modify, distribute, perform, and use photos from Unsplash for free, including for
commercial purposes, without permission from or attributing the photographer or Unsplash.
This license does not include the right to compile photos from Unsplash to replicate a similar or competing service.

Licenseページより引用
上記の通り利用規約が非常に緩い+Unsplash APIというなにやら便利なものもあるらしいのでTrelloなどのwebサービスも写真をよく使っているようです。
今回使用した写真含め、とても綺麗な写真が多いので見ているだけでも楽しいサイトですので、ぜひ活用してみてください。

2019年11月28日木曜日

knex.jsでfromに2つ以上のサブクエリを書く方法。


オフィス狛 技術部のmmm(むー)です。
今回初投稿になります。
業務ではNode.jsを使用してAPI作成をしています。

今回は、Node.jsでDB操作を行う為のknex.jsと言うクエリビルダにて、from句に2つ以上のサブクエリを記載する方法を記載します。
SQL自体はなんてことないものですが、knex.jsでの書き方がわからず検索するのに時間がかかったため備忘録も込めて。

■実行環境

// node
node -v
// v10.16.3

// Knex.js
npm list --depth=0 | grep knex
// knex@0.15.2

// SQL Server
SELECT @@VERSION
// Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64)  Nov 30 2018 12:57:58  Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) on Linux (Ubuntu 16.04.5 LTS)

■書き方

結論から記載すると、半直書きするしかありません。
ちなみにこの方法に従わなくても全て直書きする方法もあります。

const subQuery1 = knex.select('id').from('table1');
const subQuery2 = knex.select('name').from('table2');

knex
    .select('*')
    .from(
        knex.raw([
            knex.raw(subQuery1).wrap('(', ') as sub_1'),
            knex.raw(subQuery2).wrap('(', ') as sub_2'),
        ]),
    );

説明
knex.raw そのままのSQLを書くことが可能
wrap knex.rawの中で使用でき、クエリをラッピング(クエリの前後に何か挿入)できる


以上となります。
またknex.jsについて調べたことがあれば、都度更新していこうと思います。


,

2019年10月31日木曜日

Windows ServerでApacheを2.4.41へアップデートする。


オフィス狛 技術部のJoeです。

数か月前になりますが、Apache HTTP Web Server 2.4系における複数の脆弱性に対応した Apache HTTP Web Server 2.4.41 が公開されました。
https://www.apachelounge.com/

担当したプロジェクトのWindows Serverがアップデート要のバージョンであったため、Apacheのアップデートを行ったのですが、「インストーラーをダウンロードして実行」のように簡単にはいかなかったので、備忘も兼ねて記事にしてみました。

アップデートを実施した環境は以下になります。
※OSやバージョンにより手順が異なる場合があるのでご注意ください。
OS:Windows Server 2012 R2
Apache:2.4.29 → 2.4.41

Apacheのアップデート

①Apacheのダウンロード

下記サイトよりダウンロードします。
https://www.apachelounge.com/

[手順]
左のメニューから「Downloads」を選択
→ ビルドバージョン(VCxx、VSxx など)を選択
→ 対象のApacheバージョンを選択してダウンロード

※ビルドバージョンの「VCxx、VSxx」は、ビルドされたVisual Studioのバージョンになるので、ご利用の環境により選択してください。
・VC14:Visual Studio C++ 2015
・VC15:Visual Studio C++ 2017
・VS16:Visual Studio C++ 2019

②アップデート前Apacheバックアップ

アップデート前の「Apache24」フォルダを別の場所へバックアップしておきます。
※アップデート後に、ソースや設定ファイル等を移行します

③コマンドプロンプト起動(管理者)

コマンドプロンプトを管理者として実行します。

④Apacheアンインストール

コマンドプロンプトで下記のコマンドを入力し、Apacheを停止してアンインストールします。
cd [Apacheのインストール先フォルダ]\bin
httpd -k shutdown
httpd -k uninstall

⑤Apacheフォルダ配置

上記①でダウンロードしたファイルを解凍した新しいバージョンの「Apache24」フォルダを、Apacheのインストール先フォルダに配置します。(旧バージョンのフォルダはバックアップしているので、私はこのタイミングで削除しました)

⑥インストール

コマンドプロンプトで下記のコマンドを入力し、Apacheをインストールします。
cd [Apacheのインストール先フォルダ]\bin
httpd -k install

⑦設定ファイル反映

ソースや設定ファイルを、アップデート後の「Apache24」フォルダに移行します。

⑧Apacheスタート

コマンドプロンプトで下記のコマンドを入力し、Apacheをスタートします。
cd [Apacheのインストール先フォルダ]\bin
httpd -k start

⑨バージョン確認

コマンドプロンプトで下記のコマンドを入力し、Apacheをスタートします。
cd [Apacheのインストール先フォルダ]\bin
httpd -v
以下のように出力され、正しくアップデートされました。
C:\Program Files\Apache Software Foundation\Apache24\bin>httpd -v
Server version: Apache/2.4.41 (Win64)
Apache Lounge VC14 Server built:   Aug 12 2019 10:48:01

⑩サービスの設定を確認

Apacheの自動起動などを設定している場合は、念のためサービスで設定を確認しておきます。


以上でアップデートが完了になります。

,

2019年10月30日水曜日

iOSアプリのアップデートをSirenを使って実装する。


オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

iOSに限ったことではないですが、
クライアントアプリとバックエンド(API)のバージョンの整合性を取るの面倒ですよね・・・・
バックエンドの都合で、どうしてもiOSを強制的にアップデートさせたい事もあります。

弊社の場合、プロジェクトにもよりますが、独自実装を行う方法と、プラグインを使う方法を使い分けています。
独自実装を行うのは、業務の兼ね合いだったり、バックエンド(API)の都合だったり、様々です。

今回はプラグインを使うパターンを説明します。
使うプラグインは「Siren」というものです。

ArtSabintsev/Siren

他のサイトなどで、導入方法は説明あるのですが、実用的な説明をしているものは少ないので、そこにポイントを絞って説明します。

と言っても・・・・・

let siren = Siren.shared
siren.presentationManager = PresentationManager(forceLanguageLocalization: .japanese)
siren.rulesManager = RulesManager(
    majorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .force), // A.b.c.d
    minorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .option), // a.B.c.d
    patchUpdateRules: Rules(promptFrequency: .daily, forAlertType: .option), // a.b.C.d
    revisionUpdateRules: Rules(promptFrequency: .weekly, forAlertType: .skip) // a.b.c.D
)
siren.wail()

これで終わりなんですけどね。
上記は、「AppDelegate」に記載します。

まずは、下記1行で、日本語化しています。
siren.presentationManager = PresentationManager(forceLanguageLocalization: .japanese)

次にRulesManagerを使って、複数パターンのルールを設定しています。
これで、バージョンアップの内容によって、アップデートの促し方を変えることが可能です。

コメントの「A.b.c.d」は、バージョン表示を示していて、大文字の部分が、変更となる箇所を表しています。
以下で細かく説明していきます。

メジャーアップデート(A.b.c.d)の時

例えば、現在使っているアプリのバージョンが「1.0.1.1」で、
App Storeにあるアプリのバージョンが「2.0.0.0」になっている場合です。
majorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .force), // A.b.c.d

この場合は、すぐにでもアップデートさせたいので、
promptFrequency: .immediately
で、アプリ起動時にすぐさまチェックし、
forAlertType: .force
で、アップデートを強制させています。
この時のダイアログ表示は、下記となります。


マイナーアップデート(a.B.c.d)の時

例えば、現在使っているアプリのバージョンが「1.1.0.0」で、
App Storeにあるアプリのバージョンが「1.2.0.0」になっている場合です。
minorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .option), // a.B.c.d

この場合も、出来ればすぐにアップデートして欲しいので、
promptFrequency: .immediately
で、アプリ起動時にすぐさまチェックしますが、
forAlertType: .option
で、アップデートはあくまで任意としています。
(まあ、毎回ダイアログが出るので、大抵の人はアップデートしてくれますけどね)
この時のダイアログ表示は、下記となります。


パッチアップデート(a.b.C.d)の時

例えば、現在使っているアプリのバージョンが「1.1.0.0」で、
App Storeにあるアプリのバージョンが「1.1.1.0」になっている場合です。
patchUpdateRules: Rules(promptFrequency: .daily, forAlertType: .option), // a.b.C.d

この場合も、出来ればすぐにアップデートして欲しいものの、
promptFrequency: .daily
で、チェック自体を1日に1回のみ行なって、
forAlertType: .option
で、アップデートもあくまで任意としています。
(この場合でも、大抵の人は2回目か3回目のダイアログ(つまり2日、3日目)ぐらいには、アップデートしてくれます)
この時のダイアログ表示は、下記となります。(マイナーアップデートと同じです。)


リビジョンアップデート(a.b.c.D)の時

例えば、現在使っているアプリのバージョンが「1.1.0.0」で、
App Storeにあるアプリのバージョンが「1.1.0.1」になっている場合です。
revisionUpdateRules: Rules(promptFrequency: .weekly, forAlertType: .skip) // a.b.c.D

この場合は、重要なアップデートではない事(少しの機能改善など)が多いので、
promptFrequency: .weekly
で、チェック自体を1週間に1回のみ行なって、
forAlertType: .skip
で、アップデート自体をスキップする事を可能としています。
(スキップの選択肢を出してしまうと、大抵の人はスキップして、アップデートしてくれません(泣))
この時のダイアログ表示は、下記となります。


以上となります。今回は、あくまで例として挙げましたが、
プロジェクトによって、色々な設定で組み合わせを試してみてはいかがでしょうか。


,

2019年10月29日火曜日

AWSのCloudWatchで期間を指定して統計情報を取得する。


オフィス狛 技術部のHammarです。

AWSのCloudWatchはいろいろな統計やデータ解析にも非常に役立つので、AWS環境で開発されている方は頻繁に見る機会があると思いますが、基本的にはグラフを見ながら、この時間にCPU使用率が~とか、ネットワーク数が~と見たりすると思います。

ただ、グラフではなく、この期間のこのCPU使用率の「平均値」とか、ネットワーク入力数の「合計」とか見たい場合があり、それらの情報もCloudWatchで確認することができたので、備忘録として記載したいと思います。

ある期間のEC2のCPU使用率の平均値を知りたい場合

①CloudWatchメニューの「メトリクス」を選択

②「すべてのメトリクス」タブで対象のEC2インスタンスの「CPUUtilization」(CPU使用率)にチェックをいれる

③「グラフ化したメトリクス」タブの統計欄で「平均」を選択する

④「グラフのオプション」タブのウィジェットタイプで「数値」を選択し、値に「時間範囲の値」を選択する

⑤画面右上の表示範囲を設定する
 「カスタム」を選択して細かな期間で表示可能です。
例えば直近1ヶ月等の平均値を表示したい場合は「絶対値」タブで開始日時、終了日時を設定し、「適用」ボタンをクリックします。

以上の設定でEC2のCPU使用率の平均値が数値で表示されます。


同じような設定方法で、その他のメトリクスの期間内の合計や平均値の数値情報のみを取得することが可能ですので、グラフ以外の使い方として利用してみてはいかがでしょうか。

2019年10月15日火曜日

iOSアプリから Amazon ConnectとLambdaで電話をかける。


こんにちは、オフィス狛 モバイル開発担当 Aika-yuy です。
今回の投稿は、iOSアプリから AmazonConnectとLambdaで任意の番号に電話をかける方法をご紹介します。


前回までのLambdaからAmazon Connectで電話をかける方法です

①Amazon Connectで電話番号、お問い合わせフローの作成
②IAMでロールを作成
③Lambda関数の作成
④実行!!


今回の手順

①Amazon CognitoでiOSアプリからアクセス権限を作成
②CognitoのロールにLambdaのアクセス権限を追加
③iOSアプリにCognitoとLambda呼び出しの設定


①Amazon CognitoでiOSアプリからアクセス権限を作成

・TOP画面からCognitoを検索して選択





・Identity Poolを作成します









・新しいIDプールの作成









・任意の名前をつける
・認証なしのユーザーを許可にチェックして作成









・許可を押すと自動でロールが作成されます








・サンプルコードをコピーします(後ほどAppDelegateに貼り付け)















②CognitoのロールにLambdaのアクセス権限を追加

・IAMに移動します
・サイドメニューのロールを選択
・先ほど自動作成されたロールが一覧に表示されています
・Unauthの方をクリックして下さい








・インラインポリシーの追加を押して下さい







・サービスはLambdaを選択
・アクションはInvokeFunctionにチェックを入れて下さい
・ポリシーの確認ボタンを押します







・ARNの追加で以前に作った、LambdaFunctionのARNをペーストして下さい







・任意の名前をつけ、ポリシーの作成ボタンを押して下さい







・ポリシーが追加されていれば、完了です
・次はiOSアプリを作成していきます

③iOSアプリにCognitoとLambda呼び出しの設定

・今回は以下をpod installしました

pod 'AWSMobileClient', '~> 2.6.13'  # For AWSMobileClient
pod 'AWSCognito'
pod 'AWSLambda'

AppDelegateに先ほどのサンプルコードをそのまま貼り付けます

import AWSCognito//インポートを忘れずに

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.APNortheast1,identityPoolId:"identityPoolId")
        let configuration = AWSServiceConfiguration(region:.APNortheast1, credentialsProvider:credentialsProvider)
        AWSServiceManager.default().defaultServiceConfiguration = configuration
        return true
    }

viewControllerにボタンを押したらLambdaFunctionを叩くようにします

import AWSLambda//インポートを忘れずに

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    @IBAction func callButtanTapped(_ sender: Any) {
        let lambdaInvoker = AWSLambdaInvoker.default()
        
        lambdaInvoker.invokeFunction("testCall", jsonObject: nil).continueWith(block: {(task:AWSTask) -> Any? in
            if let error = task.error {
                print("Error: \(error)")
                return nil
            }
            return nil
        })
    }
}

ボタンを押すと電話がかけられるようになりました!!!

,

2019年9月30日月曜日

node.jsでの日付時刻処理(moment.jsを使ってみる) その2


オフィス狛 技術部のJoeです。

前回、moment.jsを記事にしたのですが、一部機能しか紹介できなかったので、便利だなと思った機能を追加でご紹介します。

日付時刻の比較、差分、タイムゾーンの変換は、前回の記事を参照ください。
node.jsでの日付時刻処理(moment.jsを使ってみる)

※下記の例は、moment.jsのバージョン「2.22.2」で取得した結果になります。
バージョンにより、メソッド名やオブジェクト名などが異なる場合がありますので、ご注意ください。
公式ドキュメント

【フォーマット(format)】

// フォーマットを指定
console.log(moment('2019-08-28 01:05:07').format('YYYY年MM月DD日(ddd) HH時mm分ss秒'));  // 2019年08月28日(Wed) 01時05分07秒

// 曜日を日本語表示する場合、事前にロケールを設定します
moment.locale("ja");
console.log(moment('2019-08-28 01:05:07').format('YYYY年MM月DD日(ddd) HH時mm分ss秒'));  // 2019年08月28日(水) 01時05分07秒

// フォーマットを指定して、momentオブジェクトが作成可能です
const date1 = moment("2019年08月28日(水) 01時05分07秒", "YYYY年MM月DD日(ddd) HH時mm分ss秒");
console.log(moment(date1).format('YYYY/MM/DD(ddd) HH:mm:ss'));  // 2019/08/28(水) 01:05:07

// フォーマットが英語表記の場合は、事前にロケールを変更します
moment.locale("en");
const date2 = moment("Fri Aug 28 01:05:07 am 2019", "ddd MMM DD HH:mm:ss a YYYY");
console.log(moment(date2).format('YYYY/MM/DD(ddd) HH:mm:ss'));  // 2019/08/28(Wed) 01:05:07

【加減算(add、subtract)】

// 加算
console.log(moment('2019-08-30').add(2, 'days').format('YYYY年MM月DD日(ddd)'));  // 2019年09月01日(日)

// 減算
console.log(moment('2019-08-01').subtract(2, 'days').format('YYYY年MM月DD日(ddd)'));  // 2019年07月30日(火)

// 加減算する値は、オブジェクトで指定可能です(x年xヶ月x日後など)
console.log(moment('2019-08-30').add({year :2, month :3, day :5}}).format('YYYY年MM月DD日(ddd)'));  // 2021年12月05日(日)

【開始・終了(startOf、endOf)】

// 年の開始の日
console.log(moment('2019-09-04').startOf('year').format('YYYY/MM/DD(ddd) HH:mm:ss'));  // 2019/01/01(火) 00:00:00

// 週の最後の日
console.log(moment('2019-09-04').endOf('week').format('YYYY/MM/DD(ddd) HH:mm:ss'));  // 2019/09/07(土) 23:59:59

【月の日数(daysInMonth)】

// 月の日数
console.log(moment("2020-02", "YYYY-MM").daysInMonth()) // 29

// 月末の日を取得しても同じです
console.log(moment("2020-02", "YYYY-MM").endOf('month').get('date'));  // 29

【期間内判定(isBetween)】

// 期間内の場合、trueを返します(デフォルトでは、指定した開始、終了と一致する場合、falseを返します)
console.log(moment('2019-10-19').isBetween('2019-10-19', '2019-10-25'));  // false
console.log(moment('2019-10-20').isBetween('2019-10-19', '2019-10-25'));  // true
console.log(moment('2019-10-25').isBetween('2019-10-19', '2019-10-25'));  // false

// 第3パラメータで判定基準(年、月など)を指定できます
console.log(moment('2019-10-20').isBetween('2019-09-19', '2020-11-25', 'month'));  // true
console.log(moment('2019-10-20').isBetween('2019-09-19', '2020-11-25', 'year'));  // false

// 第4パラメータで、指定した開始、終了を含む「[]」、含まない「()」を指定できます
console.log(moment('2019-10-19').isBetween('2019-10-19', '2019-11-25', null, '[]'));  // true
console.log(moment('2019-10-19').isBetween('2019-10-19', '2019-11-25', null, '(]'));  // false

【うるう年判定(isLeapYear)】

// うるう年の場合、trueを返します
console.log(moment("2020", "YYYY").isLeapYear());  // true
console.log(moment("2019", "YYYY").isLeapYear());  // false

【オブジェクトのコピー(clone)】

// 値渡しでコピーされます(参照渡しではないです)
const date1 = moment('2019-08-28 01:05:07');
const date2 = date1.clone();
// コピー元を更新
console.log(date1.year(2020).format('YYYY/MM/DD(ddd) HH:mm:ss'));  // 2020/08/28(Fri) 01:05:07
// コピー先は変更されない
console.log(date2.format('YYYY/MM/DD(ddd) HH:mm:ss'));  // 2019/08/28(Wed) 01:05:07

【ロケールをカスタマイズする】

ロケールを新たに定義して、カスタマイズすることが可能です。
// ロケール「ja」を継承して新規ロケール「my_ja」を定義
moment.defineLocale('my_ja', {
  parentLocale: 'ja',
});

// 定義を更新
moment.updateLocale('my_ja', {
  // 月(MMMM:1月、2月・・・)の定義を旧暦の月で更新
  months : ["睦月", "如月", "弥生", "卯月", "皐月", "水無月", "文月", "葉月", "長月", "神無月", "霜月", "師走"],
  // 週の最初を月曜日に更新
  week : { dow: 1 }
});

// 「my_ja」にロケールを変更
moment.locale("my_ja");

// 9月を旧暦で、周の最初を表示
console.log(moment('2019-09-04').startOf('week').format('YYYY年 MMMM DD日(ddd)'));  // 2019年 長月 02日(月)

// 2番目の引数としてnullを渡すと、定義したロケールを削除します
moment.updateLocale('my_ja', null);


日付時刻処理のライブラリは他に「dayjs」、「date-fns」、「luxon」などありますが、
「moment」は一番人気のライブラリなので、ぜひ使ってみてください。

,

2019年9月27日金曜日

ワード、エクセル、パワーポイントファイル内に貼り付けられている画像を保存する。

こんにちは、オフィス狛 デザイン部のSatoです。

「複数ある画像ファイルをエクセルに貼り付けたものが素材画像として送られてきて困った!」
「このパワーポイントに貼り付けられたロゴを保存できたらいいんだけどなあ……」
こんな風にエクセルやワードやパワーポイントに貼り付けられた画像を書き出したい、しかし方法がわからないという風に悩んだことありませんか?
今回はエクセル、ワード、パワーポイントに貼り付けられた画像の保存方法を解説します。

保存したい画像が少なめの場合は「図として保存」という機能を使うと簡単です。
保存したい貼り付けられてる画像の上で右クリックします。 出てきたメニューから「図として保存」を選択しましょう!
保存名、フォルダ、形式を選択し、保存ボタンを押せば保存完了です。


また、保存したい画像が数枚ある場合は以下の方法が便利です。
(なお、下記の手順はMacOSでの手順です)
まず、保存したい画像のあるファイルを別名コピーします。ファイルの名はどのようなものでも大丈夫です。
コピーしたファイルの名前を変更して拡張子をzipにします。(警告メッセージが出ますが「".zip"を使用」を押してください)
こうして出来上がったzipを解凍し、
解凍したファイル内に、元のファイルごとに作成されるディレクトリがあり(*1)、その中の『media』というディレクトリに画像ファイルがまとめられています。

*1)
「元ファイルがエクセルの場合は『xl』」、
「元ファイルがワードの場合は『word』」、
「元ファイルがパワーポイントの場合『ppt』」

しかし、稀にアーカイブユーティリティ(Macの場合ダブルクリックでzip解凍するとこのソフトで解凍してくれます)で解凍すると「.cpgz」という拡張子の謎のファイルが出来てしまいます。
cpgz形式のファイルをダブルクリックで解凍すると、zip形式のファイルが出来、それを解凍するとcpgz形式のファイルが……といった具合に無限ループを繰り返し、正しく解凍されません。

 調べたところ、cpgz形式のファイルが出来てしまうzipファイルはターミナルで「unzip」というコマンドを使って解凍するときちんと解凍できる、とのことだったので試してみました。
(UNIXコマンドと普段戯れていない私のような非エンジニアの方々は、デスクトップに解凍できないファイルを置いておくことを推奨します。【】の中のコマンドをC⌘でコピーして⌘Vでペーストすればなんとかなりますので……!)

デスクトップに解凍したいzipファイルを置いたので【cd Desktop/】とターミナルに入力し、エンターキーを押してデスクトップに遷移します。
(きちんと遷移したか不安な時は【ls】とターミナルに入力してみてください。 デスクトップに保存してあるファイル名がずらずらとターミナルに表示されるはずです!)
遷移後、【unzip (解凍できないファイル名).zip】(unzipとファイル名の間の半角スペースは消さないでください!)とターミナルに入力し、エンターキーを押すと処理が走るので少し待ちます。
少し待つと解凍されたファイルがデスクトップにできています! 手間はかかりましたが、どうにか保存できました!やったー!

zip解凍がうまくいかない場合にはターミナルでUNIXコマンドをぽちぽち打たなくてはいけないので、きちんとできてるのかが分かりにくいかったりと少しデザイナーにはハードルが高いですが、これら二つの方法でどうにか保存できます!
困った際は試してみてください!

, ,

2019年9月25日水曜日

ASP.NetとSqlServerでローカル環境を作ってみる。


オフィス狛 技術部のHammarです。

最近ASP.Netを主に触れているのですが、いつもテストサーバー(実際のWindowsServerのIIS上)で動作確認をしていました。が、いまさらながらローカル環境を作ってみようと思い、Dockerとwindows IISを利用して作ってみました。
基本的なプロジェクトの構成としてはローカルIISのwebサーバーとDockerの仮想DBとのデータのやり取りを実現するという内容になります。
ちなみに開発はweb画面からのアクセスではなくAPI開発で、下記のようにJSONでHTTPリクエストを行うという流れで今回やりました。

■環境

webサーバー:windows 10 IIS
DB:SqlServer(Docker)

■ローカル(winsows10)のIISを有効化する

まずはローカルのIISを有効化されているか確認する必要があります。
有効化されていない場合は下記手順で有効化します。
  1. ①コントロールパネルを開く
  2. ②プログラムを選択する
  3. ③「プログラムと機能」のWindowsの機能の有効化または無効化を選択
  4. ④「Windowsの機能」の一覧のインターネットインフォメーションサービスにチェックを入れてOKを選択
  5. ⑤デスクトップ左下のWindowsボタンを右クリック⇒コンピュータの管理を選択選択
  6. ⑥サービスとアプリケーションに「IISマネージャー」があることを確認する
  7. ⑦ブラウザにhttp://localhost/ とURLを入力し、Enterを押すと以下画面が見れる

■DockerでローカルDB環境を作る

※Dockerが既にインストールされていることが前提となります
まずはdockerを起動しておきます。
$ docker-machine start

次にdocker-compose.ymlファイルを作成し、使うDBの内容を記載します。
今回はSqlServerを使って開発したいので、下記のような記載になります。

・docker-compose.yml

version: '3'
services:
  db:
    environment:
      - MSSQL_SA_PASSWORD=password
      - ACCEPT_EULA=Y
      - MSSQL_PID=Developer
    image: microsoft/mssql-server-linux:2017-latest
    ports:
      - "1433:1433"
    volumes:
      - ./mssql/:/c/var/opt/mssql/

あとは、このdocker-compose.ymlファイルがあるディレクトリで
$ docker-compose up -d
で仮想ローカルDBを作成します。
あとはSSMS等のツールを利用し、テーブル作成等細かな設定を行います。

■IISのルートディレクトリにビルドしたファイルを設置する

visual studioでビルドしたファイルを下記IISのルートディレクトリに設置します。
C:\inetpub\wwwroot

■IISのサイトを作成する

デスクトップ左下のWindowsボタンを右クリック⇒コンピュータの管理を選択し
サービスとアプリケーションの「IISマネージャー」を選択します。

※サイトの作成方法についてはいろいろなサイトで説明があるのでここでは割愛します。

■IISサイトのサービスを開始する

サイトの「Default Web Site」で右クリック→「Webサイトの管理」→「開始」をクリックしてサイトを起動します。

あとは、作成したサイトにPOSTMAN等のツールを利用して、HTTPリクエストを投げて、正常にHTTPレスポンスが返ってくればローカル環境完成となります。

細かな部分は省いたのでかなりざっくりとした記載になってしまいましたが、ローカルIISのwebサーバーとDockerの仮想DBでローカル環境作る場合に参考になればと思います。


,