ふんばりフロントエンジニアのブログ

新米フロントエンジニアの備忘録です。ふんばり温泉タオル欲しい...

【Sourcetree】Basic認証でミスって間違ったユーザー名入れて永遠に認証できない時の対処法


Sourcetreeめっちゃ便利で使っているのですが、二段階認証の時のユーザー名の記憶制御によってちょっとハマりました。

Basic認証とか二段階認証を求められるレポジトリを相手にするときにユーザー名とパスワードを求められると思います。

この時大抵の方は「ユーザーとパス覚えとけよー」というオプションにチェックを入れていると思うのですが、間違って先頭に空白スペースとか入った状態でコピペするとそのまま永遠にその記憶が残ってしまいます。

調べていると「キーチェーン(当方Macです)でSourcetreeで引っ掛けて消せばOK」って出てきたのでやってみたのですが依然として残ったまま..

で、さらに調べると下記の記事がヒット!

https://community.atlassian.com/t5/Sourcetree-questions/SourceTree-does-not-retrieve-user-name-and-password-from/qaq-p/386585

まあ「~/Library/Application Support/SourceTree」の中の「hostusernamemap」がhostとusernameのマッピングをしているようです。

てことでこれ削除して再起動したら初期状態に戻ってユーザー名打てました。

ただすべてのマッピングをリセットしちゃうので奥の手ですよねきっと。

踏み台サーバー経由してssh接続する

普通接続先は一つだけのことが多いですよね。

ただIP制限かけられてたりするとサーバーを経由してssh接続しないといけません。

丁寧にやると「sshで踏み台サーバーログイン→踏み台サーバーからssh接続」となるわけですが、ローカルマシンからコマンド一発で踏み台サーバー経由して接続できる方法があるよう。

今回私が行なったのはsftp接続でしたがコマンド的にはこんな感じ。

sftp -o ProxyJump=[踏み台サーバー:User]@[踏み台サーバー:HostName] [最終接続サーバー:User]@[最終接続サーバー:HostName]


これ打ち込むとパスワードが二回聞かれ、打ち込めば接続できます。

「-o」オプションはsshのオプションを指定する、というもので他にも色々なオプションが指定できます。

パスワード接続許可指定ない場合はどちらもconfigのhostname使ってもアクセスできました。

sftp -o ProxyJump=[踏み台サーバー:Host] [最終接続サーバー:Host]

この方法だとローカルディレクトリが自分のマシンを指してくれるので、パッとファイル確認したい時とかは便利です。(ログイン→ssh接続だとローカルディレクトリが踏み台サーバー指すので...)

InteliJ IDEAでプロジェクトツリーの一部が見えなくなってしまったときの対処法

InteliJ IDEAを普段使いしているのですが、たまにタイトルの挙動のようになってしまうことがあります。

対処法は下記です。

1. 「cmd + ;」で「Project Preferences」を開く

2. 「Add Content Root」で開きたいプロジェクトを選択
f:id:ma1129nm:20200729153901p:plain

これで再びプロジェクトツリーが復活すると思います。

なんか意外と探すのに手間取りました...

reactstrapのModalコンポーネントでなぜかモーダルが表示されなかった時

今回React案件なのですが、その中でreactstrapのModalを使う機会が多くありました。

例えばエラーモーダルとかローディングモーダルとかですね。

その中でローディングモーダルがなぜか表示される時とされない時があってなんでだろ...って思っていたのですが意外とハマったので共有します。

まず流れとしては

  1. 非同期でデータ取得
  2. 取得開始時にstoreのloadingFlg=true
  3. 取得・初期処理終了後にstoreのloadingFlg=false

というよくある流れです。

で、一回目の取得時には特に問題なくモーダルが表示されるのですが二回目以降になると表示されなくなるんですよね。

なんでだろうなあとめちゃくちゃデバッグしてたのですが、非同期でstoreのstateを変える処理は特に問題なさそう。

じゃあModalコンポーネントに問題あるのかな、と思って確認したところどうやら「fade」がこの現象の元凶みたいでした。

<Modal isOpen={isLoading} className={className}>
      <ModalBody>
        <div className="pt-1 text-center">
          <div className="sk-three-bounce">
            <div className="sk-child sk-bounce1"></div>
            <div className="sk-child sk-bounce2"></div>
            <div className="sk-child sk-bounce3"></div>
          </div>
          <p>データを読み込んでいます...</p>
        </div>
      </ModalBody>
    </Modal>

上記を下記のように変更したらこの現象は解決しました。

<Modal isOpen={isLoading} className={className} fade={false}>
      <ModalBody>
        <div className="pt-1 text-center">
          <div className="sk-three-bounce">
            <div className="sk-child sk-bounce1"></div>
            <div className="sk-child sk-bounce2"></div>
            <div className="sk-child sk-bounce3"></div>
          </div>
          <p>データを読み込んでいます...</p>
        </div>
      </ModalBody>
    </Modal>

モーダルのタイムアウトが問題なのかな?(デフォルトでは300ms)と思ったのですが、100msとかにしてもダメだったのでfade自体がアカンみたい。

結局次フェーズに持ち越した項目でしたがとりあえず解決できてよかった..

【JavaScript】reduceとmapの使い方とスプレッド構文

最近Twitterを開設したのですが、色々勉強になることが多くもっと早めに初めておけばよかったな…と後悔しております...

と、そんな訳でTwitterで見かけたある事象に対して考えてみました。

const state = [
  { id: 1, deleted: false},
  { id: 2, deleted: false},
  { id: 3, deleted: false}
]

const comp = 2;

こんな感じのディクショナリでidがある数値と一致した時にdeleted=trueにしたい、という事象です。

投稿者の方は、下記のような処理をしてました。

state.map(item => item.id === comp ? {...item, deleted: true} : item )

ただ、なんか他の方法ないかなーという感じの投稿だったので当方reduceで実装してみました。

state.reduce((acc,val) => {
  if(val.id === comp) val.deleted = true;
  return [...acc, val];
}, [])

「mapの方が簡潔やな...」って感じですが笑

ただ、reduceだと色々カスタマイズできるのがおすすめポイントです。

例えばidだけの連想配列にしたいとか。

state.reduce((acc, val) => [...acc, {id: val.id}], [])

とにかくreduce便利なんです…

どちらもいいところがあるのでどっちがいいとかはなく、ユースケースによって使い分けるのが良さそうですね。

そしてスプレッド構文が登場しますが、これはES6で登場したもの。

iterable(反復処理可能)なものはこれで展開することができちゃいます。

オブジェクトの展開はすることができません(オブジェクトがiterabaleではないため)が、投稿者の方の例のようにオブジェクトの上書きをすることはできます。

Reactのチュートリアルとかやったことある人はstateの処理とかでやったことあるかも..?

改めて調べてみるとどんどん新しい書き方出てきて楽しいですね..

今回のサンプルは下記です。


See the Pen
reduce_with_spread
by nakajima masahiro (@nakajima333ta)
on CodePen.

react-scriptsでのenvファイル切り替えと思うように行かなかったconfig設定

現在Reactの案件を進行中です。

具体的にはCoreUIというUIフレームワークを使って管理画面を作成しています。

このCoreUIはcreate-react-appで作成されているので、ビルドにはreact-scriptsを使っているのですがreact-scripts(create-react-app)でenvファイルを切り替える際には下記のようにする必要があります。

{
// 省略
"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "start:develop": "env-cmd -f .env.development yarn start",
    "start:staging": "env-cmd -f .env.staging yarn start",
    "start:production": "env-cmd -f .env.production yarn start",
    "build:develop": "env-cmd -f .env.development yarn build",
    "build:staging": "env-cmd -f .env.staging yarn build",
    "build:production": "env-cmd -f .env.production yarn build"
  }
}

CRA.md(create-react-appのmdファイル)を見てみると、「envファイルの切り替えは「env-cmd」パッケージ使うといいぞ」的なことが書いてあったのでそうしてます。
「dotenv -e .env.development」とかでもいけるみたいですが。

で、envファイルの読み込みはうまくいったのですが「awsのconfig系を全部書くのだるいな...」と思った私は一つのjsにまとめてNODE_ENVで切り替えればいいかなと思い下記のようなコードを書きました。

const config = {
  "aws_project_region": "ap-northeast-1",
  "userPoolId": "",
  "userPoolWebClientId": "",
  "identityPoolId": "",
  "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
};

const awsmobile = () => {
  switch(process.env.NODE_ENV) {
    case 'development':
      config.userPoolId = 'ap-northeast-xxxxxxxxxx';
      config.userPoolWebClientId = 'xxxxxxxxxx';
      config.identityPoolId = 'ap-northeast-1:xxxxxxxxxx';
      break;
    case 'staging':
      config.userPoolId = 'ap-northeast-xxxxxxxxxx';
      config.userPoolWebClientId = 'xxxxxxxxxx';
      config.identityPoolId = 'ap-northeast-1:xxxxxxxxxx';
      break;
  }

  return config;
}

export default awsmobile;

こんな感じでやろうとしたら見事にハマりまして...

というのも、react-scriptsはstartかbuildでNODE_ENVが固定されて、上書きできないそうなんです。

github.com

なので、build(本番)なら「production」start(ローカル)なら「development」となってしまい、NODE_ENVでの切り替えは無理だとわかりました。

ということで大人しく.envに書きました笑

ただ、固定されることは意外と便利でローカルと本番で画像パス変えたいときとかは下記のように書くことができます。

const url = process.env.NODE_ENV === 'production'
          ? `/information/assets/${id}/${fileName}`
          : `${process.env.REACT_APP_S3_OBJECT_ROOT}/information/assets/${id}/${fileName}`

つまり大枠の環境は「start/build」で決めて、細部の環境(development/staging/production)は.envにまとめるといった形がいいのかな、と。


変なことしようとするもんじゃないですねほんと..

CognitoのライブラリはもうやめてAmplify使おうっていう話

最近Cognitoでユーザー認証して、各ユーザーグループに応じて叩けるAppSyncのクエリやらミューテーションやら変えたいな、という局面が出てきました。

ちょっとCognitoでユーザー認証する流れが初めてだったので色々調べてたんですが、結構最近の記事でもCogitoのライブラリ(SDKと言った方がいい?)「amazon-cognito-js」を使って実装してるものが多かった印象です。

AmplifyはCogitoやらAppSyncやら色々ひとまとめにしてくれているAWSサービスなのでそっち使った方がかなり楽できました。

ということでログイン処理を実装するまでの一例。

require('amazon-cognito-js');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const AWS = require('aws-sdk');

const POOL_DATA = {
  UserPoolId: 'ap-northeast-1_xxxxxxx',
  ClientId: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
};

const login = () => {
    const inputData = userData;
    const authenticationData = {
      Username: inputData.username,
      Password: inputData.password
    };

    const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
    const userDatas = {
      Username: inputData.username,
      Pool: new AmazonCognitoIdentity.CognitoUserPool(POOL_DATA),
    };

    const CognitoUser = new AmazonCognitoIdentity.CognitoUser(userDatas);

    CognitoUser.authenticateUser(authenticationDetails, {
      onSuccess: (result) => {
        const accessToken = result.getAccessToken().getJwtToken();
        console.log(result);
        console.log(accessToken);
      },
      onFailure: (err) => {
        console.log(err);
      },
    });

  }

cogitoのライブラリを使う場合は上記のような感じです。めっちゃ長いですよね。

import Amplify, { Auth} from 'aws-amplify';
import awsconfig from './aws-exports';

Amplify.configure(awsconfig);

const login = async() => {
  const res = await Auth.signIn('test', 'password');
}

amplify使うとこんな感じ。めちゃくちゃ短い。

また、AppSyncを使う際にも便利です。

import Amplify, {API, graphqlOperation} from 'aws-amplify';
import awsmobile from './aws-exports';

Amplify.configure(awsmobile);

const executeQuery = async (query, input = null) {
    return await API.graphql(graphqlOperation(query, input)).catch( (err) => err);
  }

queryに、そのままクエリを入れると動きます。めっちゃ簡単ですよね。

とにかく新しいサービスは一旦使ってみるのがいいですよね、というお話でした。