【AWS】API Gateway マッピングテンプレートの設定方法

f:id:tm200:20220417162425p:plain

AWSAPI Gatewayを介してlambda関数にHTTPヘッダやPOSTパラメータを渡す際の設定方法をメモ。
Lambda プロキシ統合にチェックを入れるだけでもパラメータを参照することはできますが、レスポンスに不要な情報が含まれてしまう為、そちらは利用せずにパラメータを渡したかった次第です。


coin-look.pages.dev


ドキュメント

マッピングテンプレートのパラメータ名については、公式に一覧があります。 docs.aws.amazon.com

HTTPヘッダの設定方法

マッピングテンプレートを以下のように設定すれば、lambda関数からAuthorizationヘッダやカスタムヘッダなどのHTTPヘッダを参照できます。
$util.escapeJavaScriptエスケープしておくのがベターです。 HTTPヘッダは以下の記述のみで参照可能ですが、URLパラメータを参照するには「メソッドリクエスト」と「統合リクエスト」の欄から「URLクエリ文字列パラメータ」を指定する必要があります。

マッピングテンプレートの記載例

#set($allParams = $input.params())
{
  "params": {
    #foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
    "$type": {
      #foreach($paramName in $params.keySet())
        "$paramName": "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
        #end
    }
    #if($foreach.hasNext),#end
    #end
  }
}

lambda関数の実装例

def lambda_handler(event, context):
    # `Authorization`ヘッダの取得
    auth_header = event.get('params', {}).get('header', {}).get('Authorization', {})
    # クエリパラメータ(GETのURLパラメータ)の取得
    p = event.get('params', {}).get('querystring', {}).get('<パラメータ名>')

POSTパラメータの設定方法

POST時のリクエストパラメータのJSONは以下のように設定すると、lambda関数から参照可能となります。

マッピングテンプレートの記載例

#set($allParams = $input.params())
{
  "params": {
    "body": $input.json('$'),      **←ココ**
    #foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
    "$type": {
      #foreach($paramName in $params.keySet())
        "$paramName": "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
        #end
    }
    #if($foreach.hasNext),#end
    #end
  }
}

lambda関数の実装例

def lambda_handler(event, context):
    # jsonボディの取得
    body = event.get('params', {}).get('body', {})
    # json物理名を指定して、各パラメータを取得
    p = body.get('<パラメータ名>')

【Python】toxで複数のPythonバージョンをテストする

f:id:tm200:20220221223803p:plain

Pythonのテストコードを実行する際に、複数のPythonバージョンで動作確認したい時があり、toxの設定が便利だったので備忘メモ。

インストール

$ pip3 install tox

tox.ini

設定ファイルにlinttestの設定を記載できます。
波括弧で複数バージョンの指定が可能です。
また、lintのルールをflake8の引数で変更可能です。

[tox]
envlist = py3{7,8,9}-{lint,test},coverage-report

[testenv]
setenv =
    COVERAGE_FILE = .coverage.{envname}
changedir = {toxinidir}/tests
install_command = \
    pip install \
    --index-url=https://xxx.xxx.xxx:8000/xxx \
    --trusted-host=xxx.xxx.xxx {opts} {packages}

deps = 
    pytest
    pytest-cov
    coverage
    mock

commands =
    pytest --cov . {posargs}

[lint]
changedir = {toxinidir}
skip_install = True
deps =
    readme_renderer
    flake8
    flake8-import-order
    pylint
    mock
    pytest

commands =
    flake8 --import-order-style=pep8 --max-complexity=11 --max-line-length=120 <モジュールのディレクトリ名> (setup.py, testsなども指定可能)

[testenv:py37-lint]
skip_install = {[lint]skip_install}
changedir = {[lint]changedir}
deps = {[lint]deps}
commands = {[lint]commands}

[testenv:py38-lint]
...py37と同様

[testenv:py39-lint]
...py37と同様

実行方法

PRなどをトリガーに、CICDのパイプラインで各Pythonバージョンのコンテナを起動して、テストを自動実行するようにするとGood😎

#lint
$ tox -e py38-lint

# test
$ tox -e py38-test

【Hive】beeline実行時のオプションについて

f:id:tm200:20211209234112j:plain

beelineでHQLを実行する際のオプション指定方法について記載します。


coin-look.pages.dev


--hiveconf

Hiveの設置をオプションで指定可能です。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' --hiveconf hive.execution.engine=tez --hiveconf tez.queue.name=unfunded

-i <設定ファイル>

指定したいオプションが複数ある場合、--hiveconfではなく-i <設定ファイルのパス>などのように別ファイルにまとめることが可能です。
セッションOPEN時の初期化処理に適用されるようです。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -i hiverc
hiverc
set hive.execution.engine=tez;
set tez.queue.name=unfunded;
set hive.exec.scratchdir=/user/xxx/tmp
.....

-f <HQLファイル>

HQLファイルを実行したい場合、-fで指定します。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -f /path/to/test.hql

--hivevar key1=value1

--hivevarで変数展開が可能です。
複数ある場合は--hivevar key1=value1 --hivevar key2=value2 ...のように続けて記載します。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -f /path/to/test.hql --hivevar yyyymmdd=20211101

バックグラウンド実行

HADOOP_CLIENT_OPTSを設定する必要があります。

$ export HADOOP_CLIENT_OPTS="-Djline.terminal=jline.UnsupportedTerminal"

【React.js】makeStylesのスタイル指定方法

f:id:tm200:20211207233515p:plain

React.js(Next.js)で実装する際にmakeStylesの利用方法を調べたので、忘れないようにメモ。
気になるものがあったら、順次追記していきます。


coin-look.pages.dev


first-child

特に迷うことは無い。

const useStyles = makeStyles({
  root: {
    '& div:first-child': {
       ....
    },
});

last-child

first-childと同様。

const useStyles = makeStyles({
  root: {
    '& div:last-child': {
       ....
    },
});

nth-child

スタイルを指定したい要素の中に&:nth-child(n)という形式で定義する。

const useStyles = makeStyles({
  root: {
    '&:nth-child(2)': {
       ....
    },
});

not

&:not(:~)という形で指定可能。  

const useStyles = makeStyles({
  root: {
    '&:not(:nth-child(2))': {
       ....
    },
});

メディアクエリ

@media (min-width:1280px)のような形で指定可能。  

const useStyles = makeStyles({
  root: {
    display: 'flex',
    '@media (max-width:1024px)': {
      display: 'block',
      ....
    },
});

【HiveQL】pyhiveとPrestoでテーブル操作

f:id:tm200:20210704143739p:plain

beelineで実行していたクエリをpyhive + Prestoで再実装した際に書き方が違う部分があった為、備忘メモ。  


coin-look.pages.dev


 LOCATIONを指定してテーブル作成

元のクエリはこちら。

CREATE EXTERNAL TABLE <テーブル名> (<カラム名> <型>, ...) ROW FORMAT DELIMITED FIELDS TERMINATED BY <区切文字、eg. '\t'> LOCATION <HDFSのパス>

pyhive + Prestoで書くと以下のようになる。

cursor = presto.connect(...).cursor()
query = "CREATE TABLE test_table (id integer, note varchar) WITH (format='TEXTFILE', textfile_field_separator='\t', external_location='/path/to/hdfs')"
cursor.execute(query)

# fetchしないとテーブル作成後の処理が上手くいかない、エラーログも見れる
print(cursor.fetchone())

cursor.execute('desc test_table')
print(cursor.fetchone())

INSERT OVERWRITE

元のクエリ。

INSERT OVERWRITE <テーブル名> SELECT sum_a, b FROM (
  SELECT SUM(a) AS sum_a, b FROM (
    SELECT a, b FROM table1 UNION ALL
    SELECT a, b FROM table2 UNION ALL
    SELECT a, b FROM table3
  ) t1 GROUP BY b
) t2 WHERE sum_a > {hivevar:a_param}

PrestoINSERT OVERWRITEは使用できません。
drop後にINSERT INTOで作成する必要があります。

docs.treasuredata.com

cursor = presto.connect(...).cursor()

# drop
cursor.execute('DROP TABLE IF EXISTS test_table')

query = 'INSERT INTO test_table (sum_a, b) WITH tmp_tb AS (
  SELECT SUM(a) AS sum_a, b FROM (
    SELECT a, b FROM table1 UNION ALL
    SELECT a, b FROM table2 UNION ALL
    SELECT a, b FROM table3
  ) GROUP BY b)
  SELECT sum_a, b FROM tmp_tb WHERE  sum_a > %s'

cursor.execute(query, (100,))
print(cursor.fetchall())

【Node.js】npmパッケージの脆弱性対応

f:id:tm200:20210930200410p:plain

CIで脆弱性チェックがエラーとなり、npm audit以外の方法で応急処置をしたのでメモ。  
修正後は要動作確認。  


coin-look.pages.dev


 npm audit

まずは npm audit 等で検出されたパッケージのレポジトリを調べ、必要であれば package.json のバージョンを修正後にnpm audit fixを実行。
node_modules や package-lock.jsonは削除した方が間違いが無さそう。

$ npm audit fix

npm-force-resolutions

github.com

npm audit fixで解決しなかった場合に、どうにかして脆弱性のあるライブラリのバージョンを修正したい場合にnpm-force-resolutionsが使用できます。
package.jsonのscripts.preinstallにnpx npm-force-resolutionsresolutionsにバージョンを修正したいパッケージ情報を記載します。
依存関係を無理やり変更させる為、使用する場合はバージョンアップ後の動作確認を良く行ってください。

{
  "name": "xxxxx",
  "version": "0.0.1",
  "description": "",
  "scripts": {
    "preinstall": "npx npm-force-resolutions",     # ここ
    "build": "..."
  },
  "dependencies": {
    "vue": "^2.6.14",
    ...
  },
  "dev-dependencies": {
    "vue-jest": "^4.0.1",
    ...
  },
  "resolutions": {
     "ansi-regex": "5.0.1"     # ここ
  }
}

Dockerfileからbuildする際にエラーとなった為、.npmrcにunsafe-perm=trueを追記して解決しました。
unsafe-perm=trueで、パッケージスクリプト(preinstall)の実行時のUID/GID切り替えを抑制します。

# .npmrc
registry=https://xxx.xxx.xxx/...
unsafe-perm=true     # ここ

【Python】urllibでプロキシ設定

f:id:tm200:20210915184401p:plain

urllibで社内のプロキシサーバーを経由して外部接続という処理を実装しました。
外部接続先のAPIBasic認証が必要で、少し詰まったのでメモ。
requestsモジュールだと、プロキシ設定 + 接続先でBasic認証が上手く動かなかった為、urllibモジュールを使用しています。
上手いやり方などあれば、教えてください😊

実装

import os
import base64
import json
import urllib.request


def exec_proxy_request(usename: str, passwd: str, token: str):

    user_pass = f'{usename}:{passwd}'
    header = {
        # アクセス先のBasic認証
        'Authorization': f"Basic {base64.b64encode(user_pass.encode('utf-8')).decode('utf-8')}"
        # プロキシサーバの認証情報、必要に応じて
        'Proxy-Authorization': f'Bearer {token}'
    }

    req = urllib.request.Request('<アクセス先URL>', headers=header, method='GET')
    # プロキシの設定
    req.set_proxy('xxx.proxy.jp', 'http')
    req.set_proxy('xxx.proxy.jp', 'https')

    with urllib.request.urlopen(req) as res:
        status_code = res.getcode()
        res_body = res.read().decode('utf-8')

        if not status_code == 200:
            raise Exception(res_body)

        json_data = json.loads(res_body)

    return json_data