(JavaScript)mapでcontinueできないので代替案を探す

はじめに

普段JSやTSで新しい配列要素を作成したい時にmapをよく利用します。
for~ofで回しながら先に用意している配列にpushしていく書き方よりも、配列を加工したい旨を明示でき、すっきり表現できると考えています。

何がしたいのか

全ての配列に対して加工する場合は問題ないですが、一部の値のみを対象に加工した配列を返却したい場面でmapは少し面倒な動作をします。

<仕様>

  • 管理者のユーザー名を「さん」をつけて収集したい

例)for~ofを利用した例

const users = [
    { name: 'sample1', age: 30, admin: true },
    { name: 'sample2', age: 20, admin: false},
];

let target = [];

for (const user of users) {
    if (!user.admin) continue;
    
    target.push(user.name + 'さん');
}


例)mapを利用した例

const users = [
    { name: 'sample1', age: 30, admin: true },
    { name: 'sample2', age: 20, admin: false},
];

const target = users.map(user => {
   // ここでcontinueしてスキップしたい、、、

    if (user.admin) {
        return user.name + 'さん';
    }
})

→ ['sample1さん', undefined]

※mapについては下記リンクを参照 developer.mozilla.org

仕様を実現しようとすると、管理者でないデータがundefinedで返却されてしまいます。
その為、map内でcontinueを利用したいとなるのですが、ご存知の通りmap内でcontinueを利用する事はできません。そこで少し工夫をして同様の動作をできるようにしてみたいと思います。

解決方法

filter関数を利用する事でundefinedが返却される問題に対応する事が可能になります。

const target = users
    .filter(user => user.admin)
    .map(user => user.name + 'さん');
→ ['sample1さん']

上記コードではfilterで管理者ユーザーのみ収集し、その後mapに収集したデータが渡されるので問題となっていたsample2ユーザーの判定時にundefinedが返却されるを解消できます。

PHPコンテナにLaravelスケジューラーの設定を追加するとnginxコンテナとの接続でエラーが出た

発生した事

cronLaravelアプリを同一コンテナに入れnginxと接続しようとすると502 Bad Gatewayが発生した。
nginx側のエラーログを確認すると以下のような内容が...

connect() failed (111: Connection refused) ... 続く

どのようなDockerfileを使っていたか

一部省略しますが以下のようなDockerfileを利用していました。

FROM php:8.0-fpm-buster


COPY ./config/cron/root /etc/cron.d/cron

RUN apt-get update \
    && apt-get -y install git libzip-dev wget cron \
    ... 省略 ...
    && chmod 0644 /etc/cron.d/cron \
    && touch /var/log/cron.log \
    && touch /var/log/sample.log

CMD cron

dockerにcronを登録する方法に関しては下記の記事を参考にさせていただきました。

zenn.dev

原因

CMDは1ファイル一回に一度しかかけず最後に記載のあるものが実行されるようです。
その為、php:8.0-fpm-busterで定義されているCMD ["php-fpm"]が実行されずこのようなエラーとなっているようです。

対応策

CMD ["php-fpm"]が実行されるようDockerfileを以下のように修正します。

FROM php:8.0-fpm-buster


COPY ./config/cron/root /etc/cron.d/cron

RUN apt-get update \
    && apt-get -y install git libzip-dev wget cron \
    ... 省略 ...
    && chmod 0644 /etc/cron.d/cron \
    && touch /var/log/cron.log \
    && touch /var/log/sample.log

CMD sh -c "cron && php-fpm"

上記でエラーを解決する事ができました。

SQL Alchemy, Alembicでマルチテナント型のデータベース生成とマイグレーションを実行する方法

概要

本業でPythonで処理を書き, ORMとしてSQL Alchemy, マイグレーションの管理をSQL Alchemy, で実施しているプロジェクトがあります。
そのプロジェクトではマルチテナント方式?(一つのユーザーに一つのスキーマーが切られているイメージです。)を採用しており、契約時に新規のスキーマー作成、マイグレーションの実施を行いたいニーズがありました。
ネットには、SQL Alchemy, Alembicを利用してテーブル作成を行う記事は存在したのですが、今回のようなデータベース自体の作成やマイグレーション先をコード上で動的に切り替える方法などは見つかりませんでしたので実装方法の殴り書きとして残しておきます。 (※注意)MariaDBを利用した為、スキーマではなく以後DBを作成と書かせていただきます。

前提条件

通常のマイグレーションは実施できる状態。 alembicのenv.pyで以下のように環境変数からDB関連の値を読み取ってurlを指定しています。

alembic/env.py

from env import DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME
from alembic import context
import models

... 略

db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
config.set_main_option("sqlalchemy.url", db_url)

... 略

この場合、環境変数で指定したDB_NAMEというデータベースにマイグレーションが実行されます。
この時点では、サーバーに入り手動でDBを作成しalembicのマイグレーション実行コマンドを叩いていました。今回はこれをあるエンドポイントを叩くと引数のDB名でDBを作成しマイグレーションまで実行してくれるように設定していきます。

実装

from env import DB_USER, DB_PASSWORD, DB_HOST, DB_PORT
from sqlalchemy_utils import create_database, database_exists
from sqlalchemy import create_engine
from alembic.config import Config
from alembic import command


def settingDatabase(database_name: str):
    DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{database_name}"
    Engine = create_engine(DB_URL, encoding="utf-8")

    if database_exists(DB_URL):
        return False

    create_database(Engine.url)
    alembic_config.set_main_option("sqlalchemy.url", DB_URL)
    command.upgrade(alembic_config, 'head')

    return database_exists(DB_URL)

alembic_config = Config('./alembic.ini')

db.pyでURLを指定するようにしたのでalembicのenvファイルではURLの指定をコメントアウト
これが残っているとdb.pyで指定したURLが上書きされる為うまく動作しません。

alembic/env.py

from env import DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME
from alembic import context
import models

... 略

# db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
# config.set_main_option("sqlalchemy.url", db_url)

... 略

あとは定義したsettingDatabaseメソッドを呼び出せば引数の値を名前にもつDBが作成されマイグレーションも実行されます。

最後に

Python、Alembic、SQL Alchemy全て今回初めて触る技術になりますのでもしかすると見当違いな実装になっているかもしれません。
何か別の方法があればぜひご教授いただけますと幸いです。

ブラウザ→webサイトに接続できなくなった... そんな時のwebエンジニア向けトラブルシューティング

お疲れ様です!おっくんです。
2022年も早1ヶ月半ほどたちましたねー。毎日が早すぎる...

記事執筆のきっかけ

私は現在、エンジニア数10名以下のベンチャー企業で勤務しています。
あるあるですが、インフラをメインで触っているエンジニアの数が少なく、インフラにおけるトラブル解決のスピードが課題となっている状況です。

つい先日、新規案件のステージング環境でURLにアクセスしても画面が表示されないトラブルが発生した際、ただ「webサイトが表示されません。」と報告するのではなく、「このような状態又は動作の為トラブルと判断した」と問題を切り分けてからインフラエンジニアに依頼をかける必要性を感じ、簡単なトラブル切り分け方法を調べたのでまとめておきたいと思います。

想定しているケース

本記事では運営している「http://www.okkun-sample.com/」というwebサイトが突然ブラウザから接続できなくなってしまったというシュチュエーションを想定しています。
クライアントのOSはMac、Webサイトは、Webサーバとその裏にあるDBサーバで構築されている前提で進めます。

記事の対象者

  • 初めてエンジニアとして働くことになった方
  • インフラ周りに苦手意識をもっているwebエンジニアさん

手順

1. クライアント側のネットワーク問題を疑う

まずはクライアント側のネットワークに問題がないか調べていきます。
といっても簡単でブラウザから「http://www.okkun-sample.com/」以外のサイトに正しくアクセスできる事を確認します。

2. ホスト名解決がされているか調査する

nslookup又はdigコマンドを利用してDNSサーバに名前解決を確認します。
このnslookupコマンドはDNSサーバに対して名前解決リクエストを送信するコマンドです。

$ nslookup www.okkun-sample.com

** server can't find www.okkun-sample.com

IPアドレスが返却されない場合は、DNSの設定、もしくはDNSサービス自体に問題があります。

3. Webサーバとの疎通を調査する

pingコマンドでWebサーバとのネットワーク疎通を確認します。

$ ping www.okkun-sample.com(IPアドレスでも可)

Webサーバからの応答が返ってこない場合は、途中のネットワークに問題があるか、Webサーバがダウンしている可能性があります。

※注意点

pingコマンドにはICMPというプロトコルが利用されます。その為、サーバ側でICMPが許可されていない場合は、pingから応答は返ってこないがサーバは正しく動作している場合もあります。

4. ポート番号が閉じられていないか調査する

telnetコマンドを利用してHTTPのウェルノウンポートである「80番」に接続してみます。

$telnet www.okkun-sample.com 80

Webサーバから応答が返ってこない場合は、ポートがセキュリティ設定などで閉じられている可能性があります。

5. アプリケーション側の問題を疑う

上記①~④までのチェックをおこなった上で接続が確認できない場合は、アプリケーション側での問題である可能性が高くなります。

所感

Webサイトに接続できない場合もアプリケーションのエラー解決と同じく、一つ一つ可能性を潰し、根本原因を探し当てるプロセスは変わりません。
今までインフラ関連は難しいと先入観をもってしまい中々手を出してきませんでしたが、いざ触ってみると面白く、さらに勉強していきたいと感じています。
もっといい方法あればコメントにてご教授いただけますと幸いです。

Laravelで用意したファイルを使ってテストする

はじめに

CTIのサービスに携わっている為、音声ファイル関連の機能に関わる事が多く自動テストで音声ファイルを扱う機能をカバーしたいと考えていました。


初期実装では、UploadedFile::fake()を利用して以下のようにダミーデータ作り対応していました。

<?php

use Illuminate\Http\UploadedFile;


$record_file = UploadedFile::fake()->create(
    name: 'record-file-sample.wav',
    mimeType: 'audio/x-wav'
 );

しかしダミーデータでは、ファイルサイズチェックなどの一部メソッドのテストが上手くいかなかった為(この辺りの調査は必要かもしれない)、自分で用意したテストデータを持つファイルをPOSTする方法を調べたのでまとめておきます。

環境

Laravel8系

実装方法

1, テスト用の音声ファイルを用意する

今回はテスト用データとして利用する場面が多くなる事を想定し、テスト用ファクトリディレクトリを作成。そこに該当データを置きます。


f:id:sommelierEngineer:20220130082441p:plain

2, Illuminate\Http\UploadedFileオブジェクトを生成

<?php
use Illuminate\Http\UploadedFile;


dummy = new UploadedFile(
    './tests/Factory/File/sample-test.wav',
    'record-file-sample.wav',
    'audio/x-wav',
    null,
    true,
);


補足

UploadFileのコンストラクタ引数についても確認しておきたいと思います。
このクラスはSymfony\Component\HttpFoundation\File\UploadedFileクラスを継承しているのでそちらを見るとコンストラクタが書かれています。

<?php

    /**
     * @param string  $path  ファイルへのパス
     * @param string  $originalName アップロードされたファイルのオリジナルファイル名
     * @param string|null $mimeType  PHP が提供するファイルの型。
     * @param int|null  $error  アップロード時のエラー定数 
     * @param bool        $test        テストモードが有効かどうか
     */
    public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, bool $test = false)
    {
        ...}

参考

stackoverflow.com

2021年を振り返る

はじめに

2019年8月にwebエンジニアとしてのキャリアをスタートし早2年半。
初めてのwebエンジニア→webエンジニア転職も を経験し、成長を感じれる部分もありました。
今年も残すところ数日となったのでそんな2021年をざっと振り返っていこうと思います。

最初に2020年の目標を振り返る

  • 自身の得意分野・興味分野を見つける
  • エンジニアとして人脈を広げる
  • 減量を成功させる

エンジニア

[1月]
TypeScript、Angularを使った開発現場にアサイン。
フロントエンドをがっつり触るのはこの案件が初めてでしたが稼働は安定していたので、業務後や週末の時間を使いながら技術のキャッチアップ を行なったのを覚えています。
2020年の11月頃から転職を考えていましたが、比較的モダンな技術を触れるという事で一旦保留にしました。

[2月]
ほぼ定時上がりの毎日でした。
所属企業の受託チームから気づいたらSESチームに名前が移動しているという珍事もありました(笑)
渡された画面仕様に沿ってレビューされる事もなく我流で実装していく毎日に、このまま続けても作業者として経験年次が増えていくだけではないかと不安が込み上げてきました。
ここで転職を決意し、本格的に転職活動をスタートさせました。

[3月]
カジュアル面談、面接を受ける日々でした。
会社選びに関しては、上流に関わりたかった、ユーザーからのフィードバックを受けたかった事から自社開発企業を中心に面談を設定。
この時期には所属企業とのやりとりほとんどなかった気がする(笑)

[4月]
転職活動の結果2社から内定をいただきました。

① 自社開発企業 Laravel/Ruby on Rails/Angular 東京 フルリモート
② 自社開発企業 Laravel/Vue.js 大阪 リモート可

ドメイン領域、給与的には東京の企業に興味がありました。ただ組織規模が少々大きかった事、フルリモートという働き方が個人的に今のレベル感で不安だった事から大阪の企業を選択しました。

[5月]
転職に向けてDDD、各種アーキテクチャをインプットメインで学習していました。
5月末にSES企業を退職。向上心の強い実務未経験メンバーと共にキャリアを過ごせた事は自分にとって大きく、前職には感謝しています。


[6月 - 8月]
現職に入社。
研修として社内で利用するサーバー管理アプリを要件定義〜リリースまで対応する事に。
3ヶ月も研修期間もらっていいんかって思ってました(笑)

入社前にDDDやアーキテクチャに関してのインプットを行なっていたものの、ゼロベースで実装するとなるとどうしていいやら。
書籍を調べながら強強エンジニアに相談しつつ実装を進めました。
この研修だけでも転職した意味があったのではないかと感じるくらいの成長を感じる事ができました。具体的には、

・DDDのモデリング手法を実際に体験できた。
・レイヤー化する意図、メリットが実際に肌感として感じる事ができた。
・自動テストをシステム全体に取り入れ、その恩恵を感じる事ができた。
・リファクタリングを通して依存度の高いコードが起こす問題を実感できた。

上記以外にも多くの収穫がありました。
巷ではよく言われてる事ですが、この研修通して手を動かして学ぶ大切さを実感する事ができました。


[9月]
DDDにさらに興味をもち関連書籍に色々と手をだしました。ただエヴァンス本に関してはなぜか手を出さず、、、

前職の同僚からの誘いで同じくらいの経験年次の方との設計談義に参加しました。
普段人と関わる事が少ない引きこもりですが、今年の目標が「エンジニアとして人脈を広げる」だった事を突如思い出し参加。
やはり同じような箇所で悩んでいる事もあれば、経験してきた事によってそれぞれ強みが違ったり。
こういった会には今後も積極的に参加していきたいですね!


[10月]
新規開発のチームにアサイン。 研修の経験を活かし、モデリング、自動テストの導入やオニオンアーキテクチャを採用し実装を進めています。
チーム内にDDDに精通した開発者がいないので、モデリング手法の共有や、技術選定・アーキテクチャ・DB設計、コードレビューも担当しています。

技術選定では、Vue3系を採用したかったのですが、Vuetifyが3系に対応していないという事でVue2系×Composition APIを採用しました。

入社時点では自分がこのようなポジションになっている事を想像もしていませんでした。

[11月]
新入社員の研修メンターをする事に。
経験できた事は非常に良かったですが、自身の実力不足を感じる場面も多かったです。(特にインフラ関連...)
技術だけでなくビジネスパーソン的にも成長する必要を感じています。
課題も感じた一方で円滑にプロジェクトを進める為の開発フローなど新たな興味分野もできました。


[12月]
引き続き新規チームでのレビュー、実装に加えて、研修のメンターを継続。
個人ではモデル駆動開発の勉強会に複数回参加し、モデル駆動開発にさらに興味を持つ事となりました。

自宅の作業椅子にエルゴヒューマンプロを導入。体の負担がかなり軽くなりました。

筋トレ

4月から9月まで減量を実施。78kg→65kg(13kg)落としました。
今回の目標は、脂肪を落とし切る事だったので仕上がり体重はかなり軽めに。70kgで仕上がるような筋肉量を手に入れたい、、、
体重の減少幅は過去1でしたが、白米ベースで減量をした事もあってかしんどさは一番なかったかも。

2021年総括

それまで渡されたタスクをただ進める事ばかりでしたが、転職後は自身で考え進めていくような仕事が増え、エンジニアの仕事がより一層楽しく感じるようになりました。
1年ちょっとの経験で転職する事を少々悩みましたが、振り返るとその決断をしてよかったな思います。

2022年の目標

・モデル駆動開発の経験をさらに積む
・サーバー周りの実務経験を積む
・チームビルディングに携わる
・LTの経験をする
・文章力向上の為、ブログ(Qiitaなど含む)を月1本更新
・ベンチプレス100kgを達成する

アジャイルについて知らない事だらけだったので『アジャイルサムライ』を読んでみた

はじめに

アジャイルってなんか短いスパンで開発するんでしょ?」

以前の私はアジャイル開発に対してこの程度の認識で、その内容をはっきりとは捉えていませんでした。
自社でも開発フローを見直したいというニーズがありこの機会にアジャイル開発を詳しく知りたいと考え『アジャイルサムライ ~達人開発者への道』を読んでみたので印象に残った内容と感想をまとめておきます。

顧客が積極的チームに関わる

書籍では、顧客が積極的に関わらない開発を「犯罪的」と嘆いています。
プロジェクトは顧客の課題を解決するものであって、その当事者の意見を継続的に取り入れられない開発が顧客のイメージから徐々ズレていく事は容易に想像できます。

プロダクト開始時に失敗が決まる

驚く事にスタートを切る前からだめになってしまうプロジェクトが世の中に多くあるようです。
その原因は、プロジェクト開始時点で思い描く成功が各々異なっている事です。この状態で開発を進めていくとシステムは完成したがニーズと合わないものになる悲惨な結末を迎える事になります。
このような状態に陥らない為、チーム内でゴール、ビジョンを共有する事が重要になります。

インセプションデッキを使うメリットを考察する

インセプションデッキでは10個の設問が用意されています。
その中でプロジェクトの全体像を捉えるものと、プロジェクトの具体的な解決策を検討する2つのフェーズに分類されます。
特にメリットを感じたのは前者で、作ろうとしているものの背後にある「なぜ」をメンバー全員が掴む事でチーム、個人の振る舞いが変わってくると思います。
誰が何をする為のシステムかを理解する事で、やる事・やらない事、優先度、画面の見せ方など的確な判断が下せるようになります。
インセプションデッキの個々の設問についてはこちらにわかりやすくまとめていただいているので参照ください。
crowd.itpropartners.com

既に動いているプロジェクトでもインセプションデッキを作るべきか

既に稼働しているプロジェクトにアジャイルを取り入れる際、インセプションデッキを作るかどうかの判断は、問題がチームの方向性にまつわる事で、チームの全員が以下の5つの質問に答えられるかが基準になります。

  • このプロジェクトにいるのはなぜか?
  • 成し遂げようとしている事は何?
  • 顧客は誰?
  • 解決すべき主要な課題は何?
  • 最終判断を下すのは誰?

プロジェクトのゴール、ビジョンの共有ができているか、チームに巻き込むべき顧客を認識できているか、決定権の所在を認識できているかをはっきりさせる為の質問に答えられる状態であれば、スタート地点には立てています。。

最後に

プロジェクトの運用フローを見直す為のノウハウを知りたいという目的で読み出した書籍でしたが、プロジェクト方向づけが一番印象に残りました。
チームで品質に責任を持ち、顧客のフィードバックを受けながら改善していく流れは開発者としてもやりがいを感じれるフローではないかと感じます。
9章以降でプロジェクト運営に関する事がかかれているので再度時間を見つけて読みたいと思います。