stripe webhook express.raw() rawBody

第一引数はバッファを欲しがってる

stripe.webhooks.constructEvent(req.body, sig, endpointSecret)

バッファの見た目

<Buffer 7b 0a 20 20 22 69 64 22 3a 20 22 65 76 74 5f 31 4a 39 35 41 55 44 52 30 4f 7a 53 72 32 74 4a 53 39 6d 34 77 70 37 77 22 2c 0a 20 20 22 6f 62 6a 65 63 ... 3342 more bytes>  

イベントを発行するとイベントオブジェクトがPOSTされる
stripe trigger payment_intent.succeeded

{
  "id": "evt_1J82DR02tJs6Bq",
  "object": "event",
  "api_version": "2020-03-02",
  "created": 1625237970,
  "data": {...},
  "type": "payment_intent.succeeded"
}

それをバッファに解析したい

だからバッファに解析できる express.raw() を使用する
また、メディアタイプをそれに合わせる
express.raw(type: 'application/json')

しかしながら解析値が格納された req.body はそのまま信用できる形式ではない
なぜなら express のドキュメントにそう書いてあるから

req.body の形状はユーザーが制御する入力に基づいているため、このオブジェクトのすべてのプロパティと値は信頼できないものであり、信頼する前に検証する必要があります。たとえば、req.body.toString() は、複数の方法で失敗する可能性があります。たとえば、複数のパーサーをスタックしている場合、req.body は異なるパーサーのものである可能性があります。バッファメソッドを呼び出す前に、req.body が Buffer であることをテストすることをお勧めします。

とりあえず検証しない
そのため、異なるパーサーに該当する以下をコメント化

// app.use(express.json());

こうすることで、解析したバッファはオブジェクトにならず、バッファのままである

Typescript UtilityTypes まとめる

React に続き TypeScript も今週から始めてみたけど、おもろい!型がおもろい!カタが! UtilityTypes について、少しずつ分析してみる。

Exclued

U に割り当て可能な T のキーを全て exclued (除外)した T を構築
type Exclude<T, U> = T extends U ? never : T

Tをstring 、Uを number とした場合
extends ? の結果 string は number の部分型ではないため false。
よって string から 除外されるものはなく、そのままプリミティブ型の string となる。

type E = Exclude<string, number>
// E の型は プリミティブ型 string

const obj: E = "sample"


TをSample 、UをExample とした場合
keyof によるそれらの union 同士のextends ? において、U に対して T の name が部分型であるため true。
よって name が never となり、それ以外の age が T となる。

type E = Exclude<keyof Sample, keyof Example> 

// E の型はリテラル型 "age"

const obj: E = "age"

type Sample = {
    name: string
    age: number
}
type Example = {
    name: string
    weight: number
}

Omit

TからKをOmit (省く)したオブジェクトにマッピング
Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
Pick と逆の動作 (Pick はTからKを選んでマッピング)
シグネチャの通り、string | number | symbol の部分型のみ省くことが可能

Exclued が使用されている

// 上の方に書いた Exclued のシグネチャ
Exclude<T, U> = T extends U ? never : T

// Omit ではその変化球
// Exclude の T を keyof の union 、U を K で表しており、全体としてMappedTypes である
 type Omit<T, K extends string | number | symbol>
 = { [P in Exclude<keyof T, K>]: T[P]; }

Omit の T を Sample、 その中から省きたい K を "name" | "age" とした場合
Exclued の K である "name" | "age" に割り当て可能な Sample である T の keyof による union は、Sample の構造より、name, age となる。
そのため、それらが除外され weight が残る。

Omit<Sample, "name" | "age">

type Sample {
    name: string,
    age: number,
    weight: number
}

残った weight は P に掛かっており、その型は T[P]であらわすことから 、つまりMappedTypes であり、in によって P が weight 、T[P] の評価により、その型は number のオブジェクトにマッピングされる。

// 型引数を決める前
type Omit<T, K extends string | number | symbol>
 = { [P in Exclude<keyof T, K>]: T[P]; }

// 型引数を決めた後
Omit<Sample, "name" | "age">
= {weight: number}

// O の型は {weight: number} 型
type O = Omit<Sample, "name" | "age">

// Sample から name, age が省かれるため weight のみ指定できる
const obj: O = {weight: 4.8}

React おもろい

React を今月から始めてみたけど、おもろい! サスペンスを使ったデータ取得までしか見てないけど、とりあえず体感したかったし、公式を参考に適当に実装(^^♪。

//エラー境界
class ErrorBoundary extends React.Component {...}

//リソース
const userResource = (userId) => {
  let status = 'pending';
  let result;

   const suspender = fetch(
  `https://jsonplaceholder.typicode.com/users/${userId}`
    )
   .then((res) => res.json())
   .then(
      (user) => {
        status = 'fulfuild';
        result = user;
      },
      (error) => {
        status = 'rejected';
        result = error;
      }
    );
   const load = () => {
    if (status === 'pending') {
      throw suspender;
    } else if (status === 'fulfuild') {
      return result;
    } else {
      throw result;
    }
  };

  return { load, userId };
};

//次のユーザID
const getNextUserId = (userId) => {
  const nextId = userId === 10 ? 1 : userId + 1;
  return nextId;
};

//ユーザ
const User = ({ resource }) => {
  const user = resource.load();
  return <>{user.name}</>;
};

//レンダー
const Sample = () => {
  const [resource, setResource] = useState(userResource(1));

  const handleClick = () => {
    setResource(userResource(getNextUserId(resource.userId)));
  };
  return (
    <>
      <ErrorBoundary>
        <Suspense fallback={<>loading...</>}>
          <User resource={resource} />
        </Suspense>
      </ErrorBoundary>
      <div>
         //クリックすると別のユーザ名が表示される
        <button onClick={handleClick}>NEXT</button>
      </div>
      <GlobalStyle />
    </>
  );
};

sass アンパサンド & 後ろ ネスト 親参照

sass の親を指定する & について、セレクタの後ろについているのを見かけた

親セレクタ {
  子セレクタ & {...}
}

このようにコンパイルされるが

子セレクタ 親セレクタ {...}

css 3では親を参照できないので、当然親のスタイルは変わりませんでした

でもこうしたらできた

<div class="A">
  <div class="hoge">
        hoge
    <div class="fuga">
        fuga
    </div>
  </div>
</div>

<div class="B">
  <div class="hoge">
         hoge
    <div class="fuga">
         fuga
    </div>
  </div>
</div>
* {
    outline: solid 1px;
    padding: 0.5rem;
}

.hoge {
    color: red;

    .fuga {
      color: blue;
    }
    .B & {
      color: orange;
    }
}

コンパイル後は .B の .hoge になる
なので逆になった

親セレクタ 子セレクタ  {...}

ネストしたエリアから .Bと&を利用して.hoge を.Bの子孫要素として指定したことになる

.B .hoge {
    color: orange;
}

B の hoge のみスタイルが変更された
f:id:m_rai:20210507185209p:plain

要するに複数のHTMLが同じ構造を取る時に、その任意のネストより上位のセレクタを指定すればよいことになる

だからもし上位のセレクタとして body を指定するなら、各ページに body が存在するので、ページレベルで前述の共通スタイルの一部変更という範囲を広げることができる ( topページは pink、about ページは black のように )

各ページの body にどのページの body か区別するために任意のセレクタを付与していることで、以下のことが可能になると思う

body.x-page & {...}

試してないけど

IntersectionObserver スクロール position: absolute ⇔ position fixed

下へスクロールしたら上へ戻るためのスクロールトップボタンを表示させたい

下へスクロールすると上部へ消えていく任意の要素を監視対象とした
スクロールトップボタンはデフォルトの設定で不可視、および位置として position: absolute を設定して調整(bottom とか right とか)
下へスクロールして任意の監視対象が消えたらボタンを可視、 position を fixed にして absolute で固定された位置からスクロールに追随させる

適当に実装してみた

f:id:m_rai:20210330145527p:plain
こんな感じで

PHP null 合体演算子 便利過ぎる

試したらできた

関数($a ?? $b = [//]);

左辺が null なら右辺を引数に渡すが
その時に変数を動的に初期化できる

つまり、名前を付けることができる

これもできた

$arr1;
foreach ($arr1 ?? $arr2 ?? $arr3 = [1, 2, 3] as $item) {
    echo $item; //123
}

なんてこった((((;゚Д゚))))ガクガクブルブル

PHP セッション 閲覧履歴

適当に実装してみた

記事をクリックする
URLのパスパラメータを記事のidとしてセッションに配列で保存

こんなURL
http://localhost/article/15

FQDN + パスパラメータを取得

$url = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

フィルタリング

$filteredUrl = filter_var($url, FILTER_VALIDATE_URL);

URLからパスを文字列で取得
例の15は2桁だけど余裕持たせて4桁

$article_id = substr($filteredUrl, 25, 4);

セッション関連の関数を色々作ってクラスにまとめた

class Session
{
  private $redis;
  public function __construct()
    {
        $this->redis = new Redis();
        $this->redis->connect("redis", (int)$_ENV["REDIS_PORT"]);
        $this->redis->auth($_ENV["REDIS_PASSWORD"]);
    }

  public static function set($key, $val)
  {
     $_SESSION[$key] = $val;
  }  

履歴キーがないなら

if (!Session::has("history")) {

履歴キーと記事idを配列で保存

Session::set("history", [$article_id]);

履歴キーがあるけど配列に今クリックした記事のidがない

} else {
    if (!in_array($article_id, Session::get("history"), true)) {

履歴の配列に記事idを追加

    Session::push("history", $article_id);
  }
}