Merge pull request #127 develop into master

This commit is contained in:
東 正剛 2022-11-02 17:32:54 +09:00
commit 65b820dd07
48 changed files with 2291 additions and 992 deletions

2
.gitignore vendored
View File

@ -3,5 +3,7 @@ package-lock.json
node_modules/ node_modules/
# ローカル確認用環境変数ファイル # ローカル確認用環境変数ファイル
.env .env
# Pythonの仮想環境ファイル
.venv
# pythonのキャッシュファイル # pythonのキャッシュファイル
__pycache__/ __pycache__/

48
ecs/dataimport/README.md Normal file
View File

@ -0,0 +1,48 @@
# データ取り込み処理
## 概要
データ取り込みバケット(`mbj-newdwh2021-<環境名>-data`)に配置されたデータファイルを、設定に基づいてデータベースに登録する処理を行う
処理順序等の詳細は設計書を参照のこと
## 環境構築
### 事前準備
- [Wiki - Python環境構築](https://nds-tyo.backlog.com/alias/wiki/1874930)の「pipenvの導入」まで完了していること
### Python仮想環境にパッケージをインストール
- このドキュメントと同じ階層でコマンドラインを開き、以下のコマンドを実行する
```sh
pipenv install -r requirements.txt
```
- 以降、依存モジュールの追加が発生した場合に、`requirements.txt`に追記した上で、上記のコマンドを実行すること
## ローカルでの実行手順
- 当ディレクトリ内に`.vscode/launch.json`を作成し、以下のコードを貼り付ける
- 既にある場合は作成不要
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: データ取り込み処理",
"type": "python",
"request": "launch",
"program": "ecs/dataimport/controller.py",
"console": "integratedTerminal",
"justMyCode": true,
"envFile": "${workspaceFolder}/.env",
}
]
}
```
- 当ディレクトリ内に`.env`ファイルを、作成し、環境変数を設定する
- 設定する環境変数は設計書を参照のこと
- F5キーを押し、処理を実行する

View File

@ -1,11 +1,13 @@
from datetime import datetime
import boto3
import io
import csv import csv
import io
import sys import sys
from datetime import datetime
import boto3
from common import convert_quotechar, debug_log
from end import end from end import end
from error import error from error import error
from common import debug_log
# 定数 # 定数
DIRECTORY_WORK = '/work/' DIRECTORY_WORK = '/work/'
@ -23,6 +25,14 @@ SETTINGS_ITEM = {
'storageSchemaName': 9, 'storageSchemaName': 9,
'loadSchemaName': 10, 'loadSchemaName': 10,
'exSqlFileName': 11, 'exSqlFileName': 11,
'commaReplaceColumns': 12,
'importManner': 13,
'reserved1': 14,
'reserved2': 15,
'reserved3': 16,
'reserved4': 17,
'reserved5': 18,
'reserved6': 19
} }
LINE_FEED_CODE = { LINE_FEED_CODE = {
'CR': '\r', 'CR': '\r',
@ -74,14 +84,18 @@ def check(bucket_name, target_data_source, target_file_name, settings_key, log_i
work_obj = s3_resource.Object(bucket_name, work_key) work_obj = s3_resource.Object(bucket_name, work_key)
work_response = work_obj.get() work_response = work_obj.get()
work_data = io.TextIOWrapper(io.BytesIO(work_response["Body"].read()), encoding=settings_list[SETTINGS_ITEM["charCode"]], newline=LINE_FEED_CODE[settings_list[SETTINGS_ITEM["lineFeedCode"]]]) work_data = io.TextIOWrapper(io.BytesIO(work_response["Body"].read()), encoding=settings_list[SETTINGS_ITEM["charCode"]], newline=LINE_FEED_CODE[settings_list[SETTINGS_ITEM["lineFeedCode"]]])
work_header_list = [] work_csv_row = []
for line in csv.reader(work_data, quotechar=settings_list[SETTINGS_ITEM["quotechar"]], delimiter=settings_list[SETTINGS_ITEM["delimiter"]]): for i, line in enumerate(csv.reader(work_data, quotechar=convert_quotechar(settings_list[SETTINGS_ITEM["quotechar"]]), delimiter=settings_list[SETTINGS_ITEM["delimiter"]])):
work_header_list = line # ヘッダあり、かつ、1行目の場合
if int(settings_list[SETTINGS_ITEM["headerFlag"]]) == 1 and i == 0:
work_csv_row.append(line)
continue
work_csv_row.append(line)
break break
# ② C-0のデータ件数チェックを開始する # ② C-0のデータ件数チェックを開始する
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-02 - C-0のチェックを開始します') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-02 - C-0のチェックを開始します')
if not len(work_header_list): if is_empty_file(work_csv_row, settings_list):
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-03 - 投入ファイルが0バイトのため処理を終了します') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-03 - 投入ファイルが0バイトのため処理を終了します')
end(bucket_name, target_data_source, target_file_name, '', log_info, mode) end(bucket_name, target_data_source, target_file_name, '', log_info, mode)
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-04 - 終了処理完了') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-04 - 終了処理完了')
@ -90,16 +104,17 @@ def check(bucket_name, target_data_source, target_file_name, settings_key, log_i
# ③ C-1の項目数チェックを開始する # ③ C-1の項目数チェックを開始する
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-06 - C-1のチェックを開始します') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-06 - C-1のチェックを開始します')
work_header_list_len = len(work_header_list) work_csv_row_item_len = len(work_csv_row[0])
if work_header_list_len == int(settings_list[SETTINGS_ITEM["csvNumItems"]]): if work_csv_row_item_len == int(settings_list[SETTINGS_ITEM["csvNumItems"]]):
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-07 - C-1正常終了') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-07 - C-1正常終了')
else: else:
raise CheckError(f'E-CHK-01 - 項目数が一致しません 個別設定ファイル項目数:{settings_list[SETTINGS_ITEM["csvNumItems"]]} 投入データ項目数:{work_header_list_len}') raise CheckError(f'E-CHK-01 - 項目数が一致しません 個別設定ファイル項目数:{settings_list[SETTINGS_ITEM["csvNumItems"]]} 投入データ項目数:{work_csv_row_item_len}')
# ④ C-2の項目並び順チェック開始する # ④ C-2の項目並び順チェック開始する
if int(settings_list[SETTINGS_ITEM["headerFlag"]]) == True: if int(settings_list[SETTINGS_ITEM["headerFlag"]]) == True:
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-08 - C-2のチェックを開始します') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-CHK-08 - C-2のチェックを開始します')
settings_header_list = settings_list[SETTINGS_ITEM["csvNameItems"]].rstrip().split(',') settings_header_list = settings_list[SETTINGS_ITEM["csvNameItems"]].rstrip().split(',')
work_header_list = work_csv_row[0]
for i in range(len(settings_header_list)): for i in range(len(settings_header_list)):
if not settings_header_list[i] == work_header_list[i]: if not settings_header_list[i] == work_header_list[i]:
raise CheckError(f'E-CHK-02 - 項目順序が一致しません {i + 1}番目の項目 個別設定ファイル項目:{settings_header_list[i]} 投入データ項目:{work_header_list[i]}') raise CheckError(f'E-CHK-02 - 項目順序が一致しません {i + 1}番目の項目 個別設定ファイル項目:{settings_header_list[i]} 投入データ項目:{work_header_list[i]}')
@ -114,3 +129,22 @@ def check(bucket_name, target_data_source, target_file_name, settings_key, log_i
except Exception as e: except Exception as e:
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["e"]} E-CHK-99 - エラー内容:{e}') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["e"]} E-CHK-99 - エラー内容:{e}')
error(bucket_name, target_data_source, target_file_name, log_info) error(bucket_name, target_data_source, target_file_name, log_info)
def is_empty_file(work_csv_row: list, settings_list: list):
"""② C-0のデータ件数チェック
ヘッダ行がある場合は1行目を読み飛ばして判定する
Args:
work_csv_row (list): CSVファイルの1行目(ヘッダを含む場合は2行目まで)
settings_list (list): 個別設定ファイルのリスト
Returns:
bool: CSVファイルの1行目が0件だった場合はTrue
"""
has_header = int(settings_list[SETTINGS_ITEM["headerFlag"]]) == 1
# ヘッダのみのファイルも0バイトファイルをみなす
if has_header:
return len(work_csv_row[1:]) == 0
return len(work_csv_row) == 0

View File

@ -11,3 +11,18 @@ MODE_TYPE = {
def debug_log(log, log_info, mode): def debug_log(log, log_info, mode):
if MODE_TYPE['d'] == mode: if MODE_TYPE['d'] == mode:
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["d"]} {log}') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["d"]} {log}')
def convert_quotechar(quotechar):
"""csvモジュールの囲い文字を変換する
Args:
quotechar : 項目囲い文字の設定値
Returns:
空文字空白文字の場合None
それ以外設定値をそのまま帰す
"""
if (quotechar.strip(' ') == ''):
return None
return quotechar

View File

@ -1,11 +1,14 @@
import csv
import io
import re
from datetime import datetime from datetime import datetime
import boto3 import boto3
import pymysql import pymysql
from pymysql.constants import CLIENT from pymysql.constants import CLIENT
import io
import csv from common import convert_quotechar, debug_log
from error import error from error import error
from common import debug_log
# 定数 # 定数
DIRECTORY_WORK = '/work/' DIRECTORY_WORK = '/work/'
@ -23,6 +26,14 @@ SETTINGS_ITEM = {
'storageSchemaName': 9, 'storageSchemaName': 9,
'loadSchemaName': 10, 'loadSchemaName': 10,
'exSqlFileName': 11, 'exSqlFileName': 11,
'commaReplaceColumns': 12,
'importManner': 13,
'reserved1': 14,
'reserved2': 15,
'reserved3': 16,
'reserved4': 17,
'reserved5': 18,
'reserved6': 19
} }
LINE_FEED_CODE = { LINE_FEED_CODE = {
'CR': '\r', 'CR': '\r',
@ -30,6 +41,9 @@ LINE_FEED_CODE = {
'CRLF': '\r\n', 'CRLF': '\r\n',
} }
DIRECTORY_SETTINGS = '/settings/' DIRECTORY_SETTINGS = '/settings/'
TRUNCATE_SRC_TABLE_SYMBOL = 'truncate_src_table:'
TRUNCATE_SRC_TABLE_IDENTIFY_SYMBOL_FORMAT = f'{TRUNCATE_SRC_TABLE_SYMBOL}[蓄積スキーマのテーブル名]'
INVALID_CONFIG_EXCEPTION_MESSAGE = f'個別設定ファイルのインポート方法に不備がありました。 インポート方法は "{TRUNCATE_SRC_TABLE_IDENTIFY_SYMBOL_FORMAT}" のように設定してください'
# クラス変数 # クラス変数
s3_client = boto3.client('s3') s3_client = boto3.client('s3')
@ -76,12 +90,18 @@ def main(bucket_name, target_data_source, target_file_name, settings_key, db_inf
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-MAIN-03 - タイムゾーンを変更しました') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-MAIN-03 - タイムゾーンを変更しました')
# ④ 個別設定ファイルのロードスキーマのテーブル名に記載されているテーブルをTRUNCATEする # ④ 個別設定ファイルのロードスキーマのテーブル名に記載されているテーブルをTRUNCATEする
# 個別設定ファイルの読み込み
settings_obj = s3_resource.Object(bucket_name, settings_key) settings_obj = s3_resource.Object(bucket_name, settings_key)
settings_response = settings_obj.get() settings_response = settings_obj.get()
settings_list = [] settings_list = []
for line in io.TextIOWrapper(io.BytesIO(settings_response["Body"].read()), encoding='utf-8'): for line in io.TextIOWrapper(io.BytesIO(settings_response["Body"].read()), encoding='utf-8'):
settings_list.append(line.rstrip('\n')) settings_list.append(line.rstrip('\n'))
# 設定ファイルに記載のない行を空文字として扱い、予約行とする
for _ in range(len(SETTINGS_ITEM) - len(settings_list)):
settings_list.append('')
# ロードスキーマのTRUNCATE
with conn.cursor() as cur: with conn.cursor() as cur:
sql_truncate = f'TRUNCATE table {settings_list[SETTINGS_ITEM["loadSchemaName"]]}' sql_truncate = f'TRUNCATE table {settings_list[SETTINGS_ITEM["loadSchemaName"]]}'
cur.execute(sql_truncate) cur.execute(sql_truncate)
@ -100,8 +120,9 @@ def main(bucket_name, target_data_source, target_file_name, settings_key, db_inf
warning_info = '' # ワーニング情報 warning_info = '' # ワーニング情報
index = 0 # ループインデックス index = 0 # ループインデックス
settings_db_columu_list = settings_list[SETTINGS_ITEM["dbColumuName"]].rstrip().split(',') settings_db_columu_list = settings_list[SETTINGS_ITEM["dbColumuName"]].rstrip().split(',')
settings_replace_comma_list = settings_list[SETTINGS_ITEM["commaReplaceColumns"]].rstrip().split(',')
for line in csv.reader(work_data, quotechar=settings_list[SETTINGS_ITEM["quotechar"]], delimiter=settings_list[SETTINGS_ITEM["delimiter"]]): for line in csv.reader(work_data, quotechar=convert_quotechar(settings_list[SETTINGS_ITEM["quotechar"]]), delimiter=settings_list[SETTINGS_ITEM["delimiter"]]):
try: try:
if int(settings_list[SETTINGS_ITEM["headerFlag"]]) == True and index == 0: if int(settings_list[SETTINGS_ITEM["headerFlag"]]) == True and index == 0:
index += 1 index += 1
@ -112,9 +133,10 @@ def main(bucket_name, target_data_source, target_file_name, settings_key, db_inf
process_count += 1 process_count += 1
# SQL文生成 # SQL文生成
query_parameter_list = []
sql = f'INSERT INTO {settings_list[SETTINGS_ITEM["loadSchemaName"]]} (' sql = f'INSERT INTO {settings_list[SETTINGS_ITEM["loadSchemaName"]]} ('
for i in range(len(settings_db_columu_list)): for db_column in settings_db_columu_list:
sql = f'{sql} {settings_db_columu_list[i]},' sql = f'{sql} {db_column},'
sql = f'{sql} file_name,' # システム項目:取込ファイル名 sql = f'{sql} file_name,' # システム項目:取込ファイル名
sql = f'{sql} file_row_cnt,' # システム項目:取込ファイル行番号 sql = f'{sql} file_row_cnt,' # システム項目:取込ファイル行番号
sql = f'{sql} delete_flg,' # システム項目:論理削除フラグ sql = f'{sql} delete_flg,' # システム項目:論理削除フラグ
@ -125,13 +147,18 @@ def main(bucket_name, target_data_source, target_file_name, settings_key, db_inf
sql = f'{sql} VALUES (' sql = f'{sql} VALUES ('
for i in range(len(line)): for i in range(len(line)):
# データ項目値が0桁より大きいかチェックする # データ項目値が0桁より大きいかチェックする
if len(line[i]) > 0: if len(line[i]) == 0:
# 0桁より大きい場合 # 0桁の場合
replace_line = line[i].replace('\\', '\\\\')
sql = f'{sql} "{replace_line}",'
else:
# 上記以外の場合
sql = f'{sql} NULL,' sql = f'{sql} NULL,'
continue
# データ項目値の変換処理(カンマ除去)
org_column_value = line[i]
current_settings_db_column_name = settings_db_columu_list[i]
column_value = convert_column_value(org_column_value, current_settings_db_column_name, settings_replace_comma_list)
# INSERT文のパラメータとそれに対応するプレースホルダーを設定する
query_parameter_list.append(column_value)
sql = f'{sql} %s,'
sql = f'{sql} "{target_file_name}",' # システム項目:取込ファイル名 sql = f'{sql} "{target_file_name}",' # システム項目:取込ファイル名
sql = f'{sql} "{index + 1}",' # システム項目:取込ファイル行番号 sql = f'{sql} "{index + 1}",' # システム項目:取込ファイル行番号
sql = f'{sql} "0",' # システム項目:論理削除フラグ sql = f'{sql} "0",' # システム項目:論理削除フラグ
@ -146,7 +173,7 @@ def main(bucket_name, target_data_source, target_file_name, settings_key, db_inf
# ロードスキーマのトランザクション開始 # ロードスキーマのトランザクション開始
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(sql) cur.execute(sql, query_parameter_list)
conn.commit() conn.commit()
normal_count += 1 normal_count += 1
except Exception as e: except Exception as e:
@ -162,6 +189,18 @@ def main(bucket_name, target_data_source, target_file_name, settings_key, db_inf
# ⑦ ロードスキーマのデータを蓄積スキーマにUPSERTする # ⑦ ロードスキーマのデータを蓄積スキーマにUPSERTする
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-MAIN-08 - ロードスキーマ({settings_list[SETTINGS_ITEM["loadSchemaName"]]})のデータを蓄積スキーマ({settings_list[SETTINGS_ITEM["storageSchemaName"]]})に登録します') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-MAIN-08 - ロードスキーマ({settings_list[SETTINGS_ITEM["loadSchemaName"]]})のデータを蓄積スキーマ({settings_list[SETTINGS_ITEM["storageSchemaName"]]})に登録します')
# インポート方法判断
try:
if truncate_judge(settings_list):
with conn.cursor() as cur:
sql_truncate = f'TRUNCATE table {settings_list[SETTINGS_ITEM["storageSchemaName"]]}'
cur.execute(sql_truncate)
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["i"]} I-MAIN-20 - {settings_list[SETTINGS_ITEM["storageSchemaName"]]} をTRUNCATEしました')
except InvalidConfigException as e:
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["e"]} E-MAIN-01 - エラー内容:{e}')
error(bucket_name, target_data_source, target_file_name, log_info)
# SQL文生成 # SQL文生成
sql = f'INSERT INTO {settings_list[SETTINGS_ITEM["storageSchemaName"]]} (' sql = f'INSERT INTO {settings_list[SETTINGS_ITEM["storageSchemaName"]]} ('
for i in range(len(settings_db_columu_list)): for i in range(len(settings_db_columu_list)):
@ -190,10 +229,10 @@ def main(bucket_name, target_data_source, target_file_name, settings_key, db_inf
sql = f'{sql} file_name=t.file_name,' # システム項目:取込ファイル名 sql = f'{sql} file_name=t.file_name,' # システム項目:取込ファイル名
sql = f'{sql} file_row_cnt=t.file_row_cnt,' # システム項目:取込ファイル行番号 sql = f'{sql} file_row_cnt=t.file_row_cnt,' # システム項目:取込ファイル行番号
sql = f'{sql} delete_flg={settings_list[SETTINGS_ITEM["storageSchemaName"]]}.delete_flg,' # システム項目:論理削除フラグ sql = f'{sql} delete_flg={settings_list[SETTINGS_ITEM["storageSchemaName"]]}.delete_flg,' # システム項目:論理削除フラグ
sql = f'{sql} ins_user=t.ins_user,' # システム項目:登録者 sql = f'{sql} ins_user={settings_list[SETTINGS_ITEM["storageSchemaName"]]}.ins_user,' # システム項目:登録者
sql = f'{sql} ins_date=t.ins_date,' # システム項目:登録日時 sql = f'{sql} ins_date={settings_list[SETTINGS_ITEM["storageSchemaName"]]}.ins_date,' # システム項目:登録日時
sql = f'{sql} upd_user={settings_list[SETTINGS_ITEM["storageSchemaName"]]}.upd_user,' # システム項目:更新者 sql = f'{sql} upd_user=t.ins_user,' # システム項目:更新者
sql = f'{sql} upd_date={settings_list[SETTINGS_ITEM["storageSchemaName"]]}.upd_date' # システム項目:更新日時 sql = f'{sql} upd_date=t.ins_date' # システム項目:更新日時
debug_log(sql, log_info, mode) debug_log(sql, log_info, mode)
@ -272,3 +311,50 @@ def connection_close(conn, bucket_name, target_data_source, target_file_name, lo
except Exception as e: except Exception as e:
print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["e"]} E-MAIN-99 - エラー内容:{e}') print(f'{datetime.now():%Y-%m-%d %H:%M:%S} {log_info} {LOG_LEVEL["e"]} E-MAIN-99 - エラー内容:{e}')
error(bucket_name, target_data_source, target_file_name, log_info) error(bucket_name, target_data_source, target_file_name, log_info)
def convert_column_value(org_column_value, current_settings_db_column_name, settings_replace_comma_list):
"""データ項目値変換処理
- 数値内のカンマ除去処理
Args:
org_column_value : 投入データの値
current_settings_db_column_name : 投入データのDBカラム物理名
settings_replace_comma_list : 投入データの数値型のDBカラム物理名のリスト
Returns:
converted_column_value:変換処理を行った投入データの値
"""
# 投入データのDB物理カラム名が設定ファイルの数値型のDBカラム物理名に含まれている場合、データ項目値の「,」を取り除く
converted_column_value = org_column_value
if current_settings_db_column_name in settings_replace_comma_list:
converted_column_value = converted_column_value.replace(',', '')
return converted_column_value
def truncate_judge(settings_list):
"""TRUNCATE処理対応判定
Args:
settings_list (list): 個別設定ファイル
Raises:
InvalidConfigException: 個別設定ファイルのインポート方法の設定ミス
Returns:
Bool: Truncate対象の場合TrueTruncate対象でない場合False
"""
# upsert判定
if not settings_list[SETTINGS_ITEM["importManner"]]:
return False
# インポート方法設定チェック
if not settings_list[SETTINGS_ITEM["importManner"]].startswith(TRUNCATE_SRC_TABLE_SYMBOL):
raise InvalidConfigException(INVALID_CONFIG_EXCEPTION_MESSAGE)
import_manner_splitted_list = settings_list[SETTINGS_ITEM["importManner"]].split(':')
if len(import_manner_splitted_list) != 2:
raise InvalidConfigException(INVALID_CONFIG_EXCEPTION_MESSAGE)
if import_manner_splitted_list[1] != settings_list[SETTINGS_ITEM["storageSchemaName"]]:
raise InvalidConfigException(INVALID_CONFIG_EXCEPTION_MESSAGE)
return True
class InvalidConfigException(Exception):
pass

View File

@ -0,0 +1,19 @@
FROM python:3.9
ENV WORKDIR /function/
ENV TZ="Asia/Tokyo"
WORKDIR ${WORKDIR}
COPY Pipfile Pipfile.lock ${WORKDIR}
RUN \
apt update -y && \
# パッケージのセキュリティアップデートのみを適用するコマンド
apt install -y unattended-upgrades && \
unattended-upgrades && \
pip install pipenv --no-cache-dir && \
pipenv install --system --deploy && \
pip uninstall -y pipenv virtualenv-clone virtualenv
COPY check-view-option ./
ENTRYPOINT [ "/usr/local/bin/python", "-m", "awslambdaric" ]
CMD [ "main.handler" ]

View File

@ -0,0 +1,17 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
awslambdaric = "*"
boto3 = "*"
pymysql = "*"
cryptography = "*"
[dev-packages]
autopep8 = "*"
flake8 = "*"
[requires]
python_version = "3.9"

View File

@ -0,0 +1,313 @@
{
"_meta": {
"hash": {
"sha256": "0bf055eba7a510de27e990db23f5203946ebbc02a6c678b89051dc0d1437444f"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"awslambdaric": {
"hashes": [
"sha256:059c7a66d4470169e01620d93f07424b80d302e3736cd11e68373f293a41e396",
"sha256:0e90053614f0e5e5d6d6ae6d164412ce95b5d549c6fb0f6ff4290d77c5e9d3e5",
"sha256:11a365164efec105aa670259dfe473d9609da8f6f2e468790b2dfc24969bfff1",
"sha256:19da28e8c892b1c52a9db4d2b986af303932e3a4c4632eb0c5d5eb6a673c6022",
"sha256:2eb2fdb1ae0f84669d37f193f247fa115a282a7777e051ced3a33620d6280646",
"sha256:2efff2292fc8f8484eb094ffd77808a67815353be898a7f0b33ce51b841af691",
"sha256:387b94cb0358662ae2b203f0aa2af25e80c6a2019a6b569f733ecd993a4f53d2",
"sha256:38f8ae67ecb5b4e9f7fc42746ee39765dd7ddab359cb7e8ebfda1de0f0c0b059",
"sha256:3fd0e1b3891987fa7ebb0c08d24c76af5fc17466f6efdfa9a59848dfb23930ec",
"sha256:63a82d21d66146b3fde7eb6086abd058b75bdcab4a02b02afe0e8e4a45edfb5b",
"sha256:676a741ad8f3aa27d651bcf3a2b83d5cee815f99c8b2b9abef3cb22ca7b29698",
"sha256:9b0781bd41c20a2f2a0b018464a1daa376f663bd5eb7b0b6ba78f483681b1519",
"sha256:bad98f2f94cecc90b89ac4e1d4feed96eb664e13c29b7ce232444cc9358e0d36",
"sha256:d64dcba8da9dbea62644133a48c75376a37bfe0f84096ad73bf7fc5b2eb31fc7",
"sha256:d8f280b25d8a7ae6b6ff92a9bbc6567b984264be8ef3e0fcb0402a1247f6c75d",
"sha256:dad646f566aa7ec9b7179f16ca6741a2bea148abec6ed5947f86d00607e0a9a2",
"sha256:dc7072f642fdd215387d4921bbd5ac91b96a4a705bce5e7853622d09fe59f57d",
"sha256:fbbd24446ce2f876335b178f04aa4ec7ec480afc0f9621ebfdd5f55ad4b7c06e",
"sha256:fe76893a1b42bcee4c91c6456092d2a42455818756e8f62d50e8c5adb22fa9e7"
],
"index": "pypi",
"version": "==2.0.4"
},
"boto3": {
"hashes": [
"sha256:626bbec91ca2423e427636db207a03c854b52d22715c9b34a953ee8260817f6f",
"sha256:7e0a5c86059866d7f9e27d6574da9bfb4f8a03c4caf055724145f3cd44785b81"
],
"index": "pypi",
"version": "==1.24.27"
},
"botocore": {
"hashes": [
"sha256:524da451350c41e3136353183e7424c95952124163ea8ec03f57f29597bbcb4b",
"sha256:583b85f8a799fb89d1a762db041163b5848b08e79cee06b609bcaaeb69ea1fa6"
],
"markers": "python_version >= '3.7'",
"version": "==1.27.27"
},
"cffi": {
"hashes": [
"sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5",
"sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef",
"sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104",
"sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426",
"sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405",
"sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375",
"sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a",
"sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e",
"sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc",
"sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf",
"sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185",
"sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497",
"sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3",
"sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35",
"sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
"sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83",
"sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21",
"sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca",
"sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984",
"sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac",
"sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd",
"sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee",
"sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a",
"sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2",
"sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192",
"sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7",
"sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585",
"sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f",
"sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e",
"sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27",
"sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b",
"sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e",
"sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e",
"sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d",
"sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c",
"sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415",
"sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82",
"sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02",
"sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314",
"sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
"sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
"sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3",
"sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914",
"sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045",
"sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d",
"sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9",
"sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5",
"sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2",
"sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c",
"sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3",
"sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2",
"sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
"sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d",
"sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d",
"sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
"sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162",
"sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76",
"sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4",
"sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e",
"sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9",
"sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6",
"sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b",
"sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01",
"sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"
],
"version": "==1.15.1"
},
"cryptography": {
"hashes": [
"sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59",
"sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596",
"sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3",
"sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5",
"sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab",
"sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884",
"sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82",
"sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b",
"sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441",
"sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa",
"sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d",
"sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b",
"sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a",
"sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6",
"sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157",
"sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280",
"sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282",
"sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67",
"sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8",
"sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046",
"sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327",
"sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"
],
"index": "pypi",
"version": "==37.0.4"
},
"jmespath": {
"hashes": [
"sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980",
"sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"
],
"markers": "python_version >= '3.7'",
"version": "==1.0.1"
},
"pycparser": {
"hashes": [
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
"version": "==2.21"
},
"pymysql": {
"hashes": [
"sha256:41fc3a0c5013d5f039639442321185532e3e2c8924687abe6537de157d403641",
"sha256:816927a350f38d56072aeca5dfb10221fe1dc653745853d30a216637f5d7ad36"
],
"index": "pypi",
"version": "==1.0.2"
},
"python-dateutil": {
"hashes": [
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.2"
},
"s3transfer": {
"hashes": [
"sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd",
"sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"
],
"markers": "python_version >= '3.7'",
"version": "==0.6.0"
},
"simplejson": {
"hashes": [
"sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667",
"sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3",
"sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043",
"sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb",
"sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0",
"sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d",
"sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8",
"sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f",
"sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf",
"sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748",
"sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278",
"sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4",
"sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a",
"sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8",
"sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d",
"sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971",
"sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841",
"sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f",
"sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b",
"sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45",
"sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9",
"sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6",
"sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc",
"sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956",
"sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d",
"sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746",
"sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a",
"sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0",
"sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25",
"sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625",
"sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995",
"sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46",
"sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f",
"sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a",
"sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139",
"sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f",
"sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da",
"sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34",
"sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b",
"sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94",
"sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04",
"sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b",
"sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396",
"sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06",
"sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb"
],
"markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.17.2"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"urllib3": {
"hashes": [
"sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
"sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
"version": "==1.26.10"
}
},
"develop": {
"autopep8": {
"hashes": [
"sha256:44f0932855039d2c15c4510d6df665e4730f2b8582704fa48f9c55bd3e17d979",
"sha256:ed77137193bbac52d029a52c59bec1b0629b5a186c495f1eb21b126ac466083f"
],
"index": "pypi",
"version": "==1.6.0"
},
"flake8": {
"hashes": [
"sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d",
"sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"
],
"index": "pypi",
"version": "==4.0.1"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"pycodestyle": {
"hashes": [
"sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20",
"sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.8.0"
},
"pyflakes": {
"hashes": [
"sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c",
"sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.0"
},
"toml": {
"hashes": [
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
}
}
}

View File

@ -0,0 +1,34 @@
import boto3
import environments
from constants import AWS_RESOURCE_S3, S3_RESPONSE_BODY, UTF8
class S3Resource:
def __init__(self, bucket_name: str) -> None:
self.__s3_resource = boto3.resource(AWS_RESOURCE_S3)
self.__s3_bucket = self.__s3_resource.Bucket(bucket_name)
def get_object(self, object_key: str):
s3_object = self.__s3_bucket.Object(object_key)
response = s3_object.get()
return response[S3_RESPONSE_BODY].read().decode(UTF8)
class ConfigBucket:
__s3_resource: S3Resource = None
def __init__(self) -> None:
self.__s3_resource = S3Resource(environments.CONFIG_BUCKET_NAME)
@property
def check_target_schema_names(self):
return self.__s3_resource.get_object(environments.CHECK_TARGET_SCHEMA_NAMES_PATH)
@property
def notice_mail_title_template(self):
return self.__s3_resource.get_object(environments.NOTICE_MAIL_TITLE_TEMPLATE_PATH)
@property
def notice_mail_body_template(self):
return self.__s3_resource.get_object(environments.NOTICE_MAIL_BODY_TEMPLATE_PATH)

View File

@ -0,0 +1,31 @@
import boto3
import environments
from constants import AWS_RESOURCE_SNS
class SNSClient:
def __init__(self) -> None:
self.__sns_client = boto3.client(AWS_RESOURCE_SNS)
def publish(self, sns_topic_arn: str, subject: str, message: str) -> None:
publish_params = {
'TopicArn': sns_topic_arn,
'Subject': subject.rstrip('\n'),
'Message': message
}
self.__sns_client.publish(**publish_params)
class SNSNotifier:
__sns_client: SNSClient = None
def __init__(self) -> None:
self.__sns_client = SNSClient()
def publish_to_mbj(self, subject: str, message: str):
self.__sns_client.publish(environments.MBJ_NOTICE_TOPIC, subject, message)
def publish_to_nds(self, error_id: str, exception: Exception):
error_message = f'{error_id} のエラーが発生しました。ご確認ください\n詳細:{exception}'
self.__sns_client.publish(environments.NDS_NOTICE_TOPIC, environments.NDS_NOTICE_TITLE, error_message)

View File

@ -0,0 +1,34 @@
import boto3
import environments
from constants import (AWS_RESOURCE_SSM, SSM_PARAMETER_RESPONSE,
SSM_PARAMETER_VALUE)
class SSMClient:
def __init__(self) -> None:
self.__ssm_client = boto3.client(AWS_RESOURCE_SSM)
def get_ssm_params(self, parameter_key: str, with_decryption: bool):
response = self.__ssm_client.get_parameter(Name=parameter_key, WithDecryption=with_decryption)
parameter_value = response[SSM_PARAMETER_RESPONSE][SSM_PARAMETER_VALUE]
return parameter_value
class SSMParameterStore:
__ssm_client: SSMClient = None
def __init__(self) -> None:
self.__ssm_client = SSMClient()
@property
def db_host(self):
return self.__ssm_client.get_ssm_params(environments.PARAM_NAME_DB_HOST, True)
@property
def db_user_name(self):
return self.__ssm_client.get_ssm_params(environments.PARAM_NAME_DB_USER_NAME, True)
@property
def db_user_password(self):
return self.__ssm_client.get_ssm_params(environments.PARAM_NAME_DB_USER_PASSWORD, True)

View File

@ -0,0 +1,47 @@
# logger
LOG_FORMAT = '[%(levelname)s]\t%(asctime)s\t%(message)s\n'
LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
LOG_LEVEL_INFO = 'INFO'
# environments
CHECK_TARGET_SCHEMA_NAMES_PATH = 'CHECK_TARGET_SCHEMA_NAMES_PATH'
CONFIG_BUCKET_NAME = 'CONFIG_BUCKET_NAME'
LOG_LEVEL = 'LOG_LEVEL'
MBJ_NOTICE_TOPIC = 'MBJ_NOTICE_TOPIC'
NDS_NOTICE_TOPIC = 'NDS_NOTICE_TOPIC'
NDS_NOTICE_TITLE = 'NDS_NOTICE_TITLE'
NOTICE_MAIL_BODY_TEMPLATE_PATH = 'NOTICE_MAIL_BODY_TEMPLATE_PATH'
NOTICE_MAIL_TITLE_TEMPLATE_PATH = 'NOTICE_MAIL_TITLE_TEMPLATE_PATH'
PARAM_NAME_DB_HOST = 'PARAM_NAME_DB_HOST'
PARAM_NAME_DB_USER_NAME = 'PARAM_NAME_DB_USER_NAME'
PARAM_NAME_DB_USER_PASSWORD = 'PARAM_NAME_DB_USER_PASSWORD'
TZ = 'TZ'
# aws
AWS_RESOURCE_S3 = 's3'
AWS_RESOURCE_SSM = 'ssm'
AWS_RESOURCE_SNS = 'sns'
S3_RESPONSE_BODY = 'Body'
SSM_PARAMETER_RESPONSE = 'Parameter'
SSM_PARAMETER_NAME = 'Name'
SSM_PARAMETER_VALUE = 'Value'
RESPONSE_ERROR = 'Error'
RESPONSE_ERROR_CODE = 'Code'
RESPONSE_CODE_NO_SUCH_KEY = 'NoSuchKey'
RESPONSE_CODE_PARAMETER_NOT_FOUND = 'ParameterNotFound'
# sql
DEFAULT_SCHEMA = 'INFORMATION_SCHEMA'
INFORMATION_SCHEMA_SECURITY_TYPE_INVOKER = 'INVOKER'
CONNECTION_TIMEOUT = 5
# system var
UTF8 = 'utf-8'
LAUNCH_ON_LOCAL = 'local'
CHECK_TARGET_SCHEMAS = 'check_target_schemas'
# メール本文に出力する不足ファイル名一覧のインデント
MAIL_INDENT = '\n  '
# JSONファイル上のコメント業を表すシンボル
JSON_COMMENT_SYMBOL = '#'
# JSON内のコメントを置き換える正規表現
REPLACE_COMMENT_REGEX = rf'\s(?!\"){JSON_COMMENT_SYMBOL}[\s\S]*?.*'

View File

@ -0,0 +1,40 @@
import contextlib
import pymysql
from pymysql.constants import CLIENT
from constants import CONNECTION_TIMEOUT, DEFAULT_SCHEMA
class Database:
__connection: pymysql.Connection = None
__host: str = None
__user: str = None
__password: str = None
__database: str = None
def __init__(self, host: str, user: str, password: str) -> None:
self.__host = host
self.__user = user
self.__password = password
self.__database = DEFAULT_SCHEMA
def connect(self):
connection = pymysql.connect(host=self.__host, user=self.__user, passwd=self.__password,
database=self.__database, connect_timeout=CONNECTION_TIMEOUT,
client_flag=CLIENT.MULTI_STATEMENTS)
self.__connection = connection
@contextlib.contextmanager
def query(self, query: str):
if self.__connection is None:
raise Exception('データベースに接続されていません')
with self.__connection.cursor() as cursor:
cursor.execute(query)
yield cursor
def close(self):
self.__connection.close()
self.__connection = None

View File

@ -0,0 +1,8 @@
from dataclasses import dataclass
@dataclass
class NoSecurityOptionView:
schema_name: str
table_name: str
definer: str

View File

@ -0,0 +1,24 @@
import os
from constants import (CHECK_TARGET_SCHEMA_NAMES_PATH, CONFIG_BUCKET_NAME,
LOG_LEVEL, LOG_LEVEL_INFO, MBJ_NOTICE_TOPIC,
NDS_NOTICE_TITLE, NDS_NOTICE_TOPIC,
NOTICE_MAIL_BODY_TEMPLATE_PATH,
NOTICE_MAIL_TITLE_TEMPLATE_PATH, PARAM_NAME_DB_HOST,
PARAM_NAME_DB_USER_NAME, PARAM_NAME_DB_USER_PASSWORD,
TZ)
LOG_LEVEL = os.environ.get(LOG_LEVEL, LOG_LEVEL_INFO)
CHECK_TARGET_SCHEMA_NAMES_PATH = os.environ[CHECK_TARGET_SCHEMA_NAMES_PATH]
CONFIG_BUCKET_NAME = os.environ[CONFIG_BUCKET_NAME]
MBJ_NOTICE_TOPIC = os.environ[MBJ_NOTICE_TOPIC]
NDS_NOTICE_TOPIC = os.environ[NDS_NOTICE_TOPIC]
NDS_NOTICE_TITLE = os.environ[NDS_NOTICE_TITLE]
NOTICE_MAIL_BODY_TEMPLATE_PATH = os.environ[NOTICE_MAIL_BODY_TEMPLATE_PATH]
NOTICE_MAIL_TITLE_TEMPLATE_PATH = os.environ[NOTICE_MAIL_TITLE_TEMPLATE_PATH]
PARAM_NAME_DB_HOST = os.environ[PARAM_NAME_DB_HOST]
PARAM_NAME_DB_USER_NAME = os.environ[PARAM_NAME_DB_USER_NAME]
PARAM_NAME_DB_USER_PASSWORD = os.environ[PARAM_NAME_DB_USER_PASSWORD]
TZ = os.environ[TZ]

View File

@ -0,0 +1,39 @@
from abc import ABCMeta
class MeDaCaException(Exception, metaclass=ABCMeta):
"""MeDaCaシステム固有のカスタムエラークラス"""
def __init__(self, error_id: str, message) -> None:
super().__init__(message)
self.error_id = error_id
class FileNotFoundException(MeDaCaException):
"""S3のファイルが見つからない場合の例外"""
pass
class ParameterNotFoundException(MeDaCaException):
"""パラメータストアのキーが見つからない場合の例外"""
pass
class DatabaseConnectionException(MeDaCaException):
"""データベース接続に失敗した場合の例外"""
pass
class QueryExecutionException(MeDaCaException):
"""クエリ実行に失敗した場合の例外"""
pass
class SNSPublishException(MeDaCaException):
"""AmazonSNSへの通知に失敗した場合の例外"""
pass
class JSONParseException(MeDaCaException):
"""JSONのパースに失敗した場合の例外"""
pass

View File

@ -0,0 +1,17 @@
import json
import re
from constants import REPLACE_COMMENT_REGEX
class JSONParser:
__json_str: str = None
def __init__(self, json_str: str) -> None:
self.__json_str = json_str
def parse(self):
# コメントを除去して辞書に変換する
without_comment = re.sub(REPLACE_COMMENT_REGEX, '', self.__json_str)
return json.loads(without_comment)

View File

@ -0,0 +1,296 @@
"""
Viewセキュリティオプション付与チェック用Lambda関数のエントリーポイント
"""
import botocore
from aws.s3 import ConfigBucket
from aws.sns import SNSNotifier
from aws.ssm import SSMParameterStore
from constants import (CHECK_TARGET_SCHEMAS,
INFORMATION_SCHEMA_SECURITY_TYPE_INVOKER, MAIL_INDENT,
RESPONSE_CODE_NO_SUCH_KEY,
RESPONSE_CODE_PARAMETER_NOT_FOUND, RESPONSE_ERROR,
RESPONSE_ERROR_CODE)
from database import Database
from dto.no_security_option_view import NoSecurityOptionView
from environments import (CONFIG_BUCKET_NAME, MBJ_NOTICE_TOPIC,
NDS_NOTICE_TOPIC, NOTICE_MAIL_BODY_TEMPLATE_PATH,
NOTICE_MAIL_TITLE_TEMPLATE_PATH)
from exceptions import (DatabaseConnectionException, FileNotFoundException,
JSONParseException, MeDaCaException,
ParameterNotFoundException, QueryExecutionException,
SNSPublishException)
from json_perser import JSONParser
from medaca_logger import MeDaCaLogger
logger = MeDaCaLogger.get_logger()
def handler(event, context):
try:
# ① 処理開始ログを出力する
logger.info('I-01-01', '処理開始 Viewセキュリティオプション付与チェック')
# ② 設定ファイル[チェック対象スキーマ名ファイル]を読み込む
logger.info('I-02-01', 'チェック対象スキーマ名ファイルを読み込み 開始')
check_target_schemas = read_check_target_schemas()
logger.info('I-02-02', f'チェック対象スキーマ名ファイルを読み込み 終了 チェック対象スキーマ名:{check_target_schemas}')
# ③ データベースに接続する
logger.info('I-03-01', 'データベースへの接続開始 開始')
# DB接続のためのパラメータ取得
db_host, db_user_name, db_user_password = read_db_param_from_parameter_store()
connection = connection_database(db_host, db_user_name, db_user_password)
logger.info('I-03-02', 'データベースへの接続開始 成功')
# ④ Viewのオプションを確認するため、データを取得する
logger.info('I-04-01', 'Viewセキュリティオプション チェック開始')
check_result = fetch_view_security_options(connection, check_target_schemas)
logger.debug('D-04-01', f'取得データ:{check_result}')
if len(check_result) == 0:
logger.info('I-04-02', 'Viewセキュリティオプション 未設定のViewはありません。処理を終了します。')
return
logger.info('I-05-01', 'Viewセキュリティオプション 未設定のViewがあるため、メール送信処理を開始します。')
# ⑤ 取得できたデータをもとに、メール通知する文言を作成する
no_security_option_views = [NoSecurityOptionView(*row) for row in check_result]
logger.info(
'I-05-02', f'通知メール(タイトル)テンプレートファイル読込 読込元:{CONFIG_BUCKET_NAME}/{NOTICE_MAIL_TITLE_TEMPLATE_PATH}')
mail_title = read_mail_title()
logger.info(
'I-05-03', '通知メール(タイトル)テンプレートファイルを読み込みました')
logger.info(
'I-05-04', f'通知メール(本文)テンプレートファイル読込 読込元:{CONFIG_BUCKET_NAME}/{NOTICE_MAIL_BODY_TEMPLATE_PATH}')
mail_body_template = read_mail_body_template()
logger.info(
'I-05-05', '通知メール(本文)テンプレートファイルを読み込みました')
mail_body = make_notice_mail_body(no_security_option_views, mail_body_template)
logger.info('I-05-06', f'メール送信指示をします 送信先トピック:{MBJ_NOTICE_TOPIC}')
notice_to_mbj(mail_title, mail_body)
logger.info('I-05-07', 'メール送信指示をしました')
except MeDaCaException as e:
logger.exception(e.error_id, e)
logger.error('E-ERR-01', f'処理異常通知の送信指示をしました 通知先トピック:{NDS_NOTICE_TOPIC}')
notice_to_nds(e.error_id, e)
raise e
except Exception as e:
logger.exception('E-99', f'想定外のエラーが発生しました エラー内容:{e}')
logger.error('E-ERR-01', f'処理異常通知の送信指示をしました 通知先トピック:{NDS_NOTICE_TOPIC}')
notice_to_nds('E-99', e)
raise e
finally:
# ⑥ 処理終了ログを出力する
logger.info('I-06-01', '処理終了 Viewセキュリティオプション付与チェック')
def read_check_target_schemas() -> list:
"""設定ファイル[チェック対象スキーマ名ファイル]を読み込む
Raises:
FileNotFoundException: ファイルが読み込めなかったエラー
JSONParseException: JSONを辞書オブジェクトに変換できなかったエラー
Exception: 想定外のエラー
Returns:
list: チェック対象のスキーマ名のリスト
"""
try:
config_bucket = ConfigBucket()
check_target_schema_names = config_bucket.check_target_schema_names
except botocore.exceptions.ClientError as e:
if e.response[RESPONSE_ERROR][RESPONSE_ERROR_CODE] == RESPONSE_CODE_NO_SUCH_KEY:
raise FileNotFoundException('E-02-01', f'チェック対象スキーマ名ファイルの読み込みに失敗しました エラー内容:{e}')
else:
raise Exception(e)
try:
json_parser = JSONParser(check_target_schema_names)
check_target_schemas = json_parser.parse()[CHECK_TARGET_SCHEMAS]
except Exception as e:
raise JSONParseException('E-02-01', f'チェック対象スキーマ名ファイルの読み込みに失敗しました エラー内容:{e}')
return check_target_schemas
def read_db_param_from_parameter_store() -> tuple:
"""パラメータストアからDB接続情報を取得する
Raises:
ParameterNotFoundException: 指定されたパラメータが存在しないエラー
Exception: 想定外のエラー
Returns:
tuple: DB接続情報
"""
try:
parameter_store = SSMParameterStore()
db_host = parameter_store.db_host
db_user_name = parameter_store.db_user_name
db_user_password = parameter_store.db_user_password
return db_host, db_user_name, db_user_password
except botocore.exceptions.ClientError as e:
if e.response[RESPONSE_ERROR][RESPONSE_ERROR_CODE] == RESPONSE_CODE_PARAMETER_NOT_FOUND:
raise ParameterNotFoundException('E-03-01', f'パラメータストアの取得に失敗しました エラー内容:{e}')
else:
raise Exception(e)
def connection_database(host: str, user_name: str, password: str) -> Database:
"""データベース接続
Args:
host (str): DBホスト
user_name (str): DBユーザー名
password (str): DBパスワード
Raises:
DatabaseConnectionException: データベースへの接続に失敗したエラー
Returns:
Database: データベース操作クラス
"""
try:
database = Database(host, user_name, password)
database.connect()
return database
except Exception as e:
raise DatabaseConnectionException('E-03-02', f'データベースへの接続に失敗しました エラー内容:{e}')
def fetch_view_security_options(connection: Database, check_target_schemas: list) -> tuple:
"""SECURITY INVOKERのついていないViewの一覧を取得する
Args:
connection (Database): 接続済みDB操作クラス
check_target_schemas (str): チェック対象のスキーマ一覧
Raises:
QueryExecutionException: クエリ実行エラー
Returns:
tuple: クエリ実行結果
"""
select_view_security_option_sql = f"""
SELECT
TABLE_SCHEMA,
TABLE_NAME,
DEFINER
FROM
INFORMATION_SCHEMA.VIEWS
WHERE
TABLE_SCHEMA IN (
{','.join([f"'{schema_name}'" for schema_name in check_target_schemas])}
)
AND SECURITY_TYPE <> '{INFORMATION_SCHEMA_SECURITY_TYPE_INVOKER}'
"""
try:
with connection.query(select_view_security_option_sql) as cursor:
result = cursor.fetchall()
connection.close()
return result
except Exception as e:
raise QueryExecutionException('E-04-01', f'Viewセキュリティオプションチェックに失敗しました エラー内容{e}')
def make_notice_mail_body(no_security_option_views: list[NoSecurityOptionView], mail_body_template: str) -> tuple[str]:
"""メール本文を生成します
Args:
view_security_options (list[NoSecurityOptionView]): チェック対象のビュー一覧
mail_body_template (str): メール本文のテンプレート
Returns:
tuple[str]: メール本文
"""
# メール本文に埋め込むView名と作成ユーザー(DEFINER)をリストに格納
schema_view_definers = []
for option in no_security_option_views:
view_fullname = f'{option.schema_name}.{option.table_name}'
schema_view_definers.append(f'{view_fullname} View作成ユーザー{option.definer}')
# インデントした上でメール本文に打ち出し
mail_message = MAIL_INDENT.join(schema_view_definers)
mail_body = mail_body_template.format(no_option_views=mail_message)
return mail_body
def read_mail_title() -> str:
"""メールタイトルを読み込む
Raises:
FileNotFoundException: ファイルが読み込めなかったエラー
Exception: 想定外のエラー
Returns:
str: メールタイトル
"""
try:
config_bucket = ConfigBucket()
mail_title = config_bucket.notice_mail_title_template
except botocore.exceptions.ClientError as e:
if e.response[RESPONSE_ERROR][RESPONSE_ERROR_CODE] == RESPONSE_CODE_NO_SUCH_KEY:
raise FileNotFoundException('E-05-01', f'通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}')
else:
raise Exception(e)
return mail_title
def read_mail_body_template() -> str:
"""メール本文を読み込む
Raises:
FileNotFoundException: ファイルが読み込めなかったエラー
Exception: 想定外のエラー
Returns:
str: メール本文
"""
try:
config_bucket = ConfigBucket()
mail_body_template = config_bucket.notice_mail_body_template
except botocore.exceptions.ClientError as e:
if e.response[RESPONSE_ERROR][RESPONSE_ERROR_CODE] == RESPONSE_CODE_NO_SUCH_KEY:
raise FileNotFoundException('E-05-02', f'通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}')
else:
raise Exception(e)
return mail_body_template
def notice_to_mbj(mail_title: str, mail_body: str) -> None:
"""MBJへ通知を行います
Args:
mail_title (str): メールタイトル
mail_body (str): メール本文
Raises:
SNSPublishException: SNSでの通知失敗した場合のエラー
"""
try:
notifier = SNSNotifier()
notifier.publish_to_mbj(mail_title, mail_body)
except Exception as e:
raise SNSPublishException('E-98', f'通知の送信指示に失敗しました エラー内容:{e}')
def notice_to_nds(error_id: str, error_message: str) -> None:
"""NDSに処理以上通知を行う
Args:
error_id (str): エラーID
error_message (str): エラーメッセージ
Raises:
SNSPublishException: SNSでの通知失敗した場合のエラー
"""
try:
notifier = SNSNotifier()
notifier.publish_to_nds(error_id, error_message)
except Exception as e:
raise SNSPublishException('E-98', f'通知の送信指示に失敗しました エラー内容:{e}')
# ローカル実行用
if __name__ == '__main__':
handler({}, {})

View File

@ -0,0 +1,60 @@
import datetime
import logging
import sys
from zoneinfo import ZoneInfo
from constants import LAUNCH_ON_LOCAL, LOG_DATE_FORMAT, LOG_FORMAT
from environments import LOG_LEVEL, TZ
class SingletonLogger:
__logger: logging.Logger = None
def __init__(self) -> None:
# logger設定
logger = logging.getLogger()
formatter = logging.Formatter(
LOG_FORMAT,
LOG_DATE_FORMAT
)
formatter.converter = lambda *arg: datetime.datetime.now(ZoneInfo(TZ)).timetuple()
# ローカル環境で動かす場合、標準出力ハンドラーを追加する
# AWS Lambda上では`LambdaLoggerHandler`がデフォルトでセットされている
if len(sys.argv) == 2 and sys.argv[1] == LAUNCH_ON_LOCAL:
localHandler = logging.StreamHandler()
logger.addHandler(localHandler)
for handler in logger.handlers:
handler.setFormatter(formatter)
level = logging.getLevelName(LOG_LEVEL)
logger.setLevel(level)
self.__logger = logger
def debug(self, log_id: str, msg: str):
self._log(logging.DEBUG, log_id, msg)
def info(self, log_id: str, msg: str):
self._log(logging.INFO, log_id, msg)
def warning(self, log_id: str, msg: str):
self._log(logging.WARNING, log_id, msg)
def error(self, log_id: str, msg: str):
self._log(logging.ERROR, log_id, msg)
def exception(self, log_id: str, msg: str):
self._log(logging.ERROR, log_id, msg, exc_info=True)
def _log(self, log_level: int, log_id: str, msg: str, exc_info=False):
self.__logger.log(log_level, f'{log_id} {msg}', exc_info=exc_info)
class MeDaCaLogger:
__unique_instance: logging.Logger = None
@staticmethod
def get_logger() -> SingletonLogger:
# インスタンス未生成の場合、唯一のインスタンスを生成する
if not MeDaCaLogger.__unique_instance:
MeDaCaLogger.__unique_instance = SingletonLogger()
return MeDaCaLogger.__unique_instance

View File

@ -1,12 +1,11 @@
import logging
import os
import boto3
import gnupg
import datetime import datetime
import logging import logging
import os
from abc import * from abc import *
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
import traceback
import boto3
import gnupg
# 環境変数 # 環境変数
SECRET_KEY_FILE_BUCKET_NAME = os.environ["SECRET_KEY_FILE_BUCKET_NAME"] SECRET_KEY_FILE_BUCKET_NAME = os.environ["SECRET_KEY_FILE_BUCKET_NAME"]
@ -23,6 +22,7 @@ LOG_LEVEL = os.environ["LOG_LEVEL"]
# 定数 # 定数
DIRECTORY_RECV = '/recv/' DIRECTORY_RECV = '/recv/'
DIRECTORY_RECV_ERROR = '/recv_error/' DIRECTORY_RECV_ERROR = '/recv_error/'
DIRECTORY_TARGET = '/target/'
EXTENSION_ERROR = '.error' EXTENSION_ERROR = '.error'
EXTENSION_DECRYPT_ERROR = '.decrypt_error' EXTENSION_DECRYPT_ERROR = '.decrypt_error'
INDEX_EXTENSION_DELETE_NUM = -4 INDEX_EXTENSION_DELETE_NUM = -4
@ -42,8 +42,12 @@ sns_client = boto3.client('sns')
# logger設定 # logger設定
logger = logging.getLogger() logger = logging.getLogger()
def custome_time(*arg): def custome_time(*arg):
return datetime.datetime.now(ZoneInfo("Asia/Tokyo")).timetuple() return datetime.datetime.now(ZoneInfo("Asia/Tokyo")).timetuple()
formatter = logging.Formatter( formatter = logging.Formatter(
'[%(levelname)s]\t%(asctime)s\t%(message)s\n', '[%(levelname)s]\t%(asctime)s\t%(message)s\n',
'%Y-%m-%d %H:%M:%S' '%Y-%m-%d %H:%M:%S'
@ -78,7 +82,7 @@ def handler(event, context):
s3_client.download_file(s3_event.bucket_name, s3_event.file_path, PATH_TEMP_ENCRYPT_FILE) s3_client.download_file(s3_event.bucket_name, s3_event.file_path, PATH_TEMP_ENCRYPT_FILE)
logger.info('I-03-02 PGP暗号化ファイルを読み込みました') logger.info('I-03-02 PGP暗号化ファイルを読み込みました')
except Exception as e: except Exception as e:
logger.error(f'E-03-01 PGP暗号化ファイルの読み込みに失敗しました エラー内容{e}') logger.exception(f'E-03-01 PGP暗号化ファイルの読み込みに失敗しました エラー内容{e}')
raise EncryptFileReadException('E-03-01', EXTENSION_ERROR, e) raise EncryptFileReadException('E-03-01', EXTENSION_ERROR, e)
# ④ S3から秘密鍵ファイルを読み込む # ④ S3から秘密鍵ファイルを読み込む
@ -87,7 +91,7 @@ def handler(event, context):
s3_client.download_file(SECRET_KEY_FILE_BUCKET_NAME, SECRET_KEY_FILE_PATH, PATH_TEMP_PRIVATE_KEY) s3_client.download_file(SECRET_KEY_FILE_BUCKET_NAME, SECRET_KEY_FILE_PATH, PATH_TEMP_PRIVATE_KEY)
logger.info('I-04-02 秘密鍵ファイルを読み込みました') logger.info('I-04-02 秘密鍵ファイルを読み込みました')
except Exception as e: except Exception as e:
logger.error(f'E-04-01 秘密鍵ファイルの読み込みに失敗しました エラー内容:{e}') logger.exception(f'E-04-01 秘密鍵ファイルの読み込みに失敗しました エラー内容:{e}')
raise FileReadException('E-04-01', EXTENSION_ERROR, e) raise FileReadException('E-04-01', EXTENSION_ERROR, e)
# ⑤ 「③」で読み込んだ秘密鍵ファイルをPGPライブラリにインポートを行う # ⑤ 「③」で読み込んだ秘密鍵ファイルをPGPライブラリにインポートを行う
@ -98,7 +102,7 @@ def handler(event, context):
gpg.import_keys(key_file.read()) gpg.import_keys(key_file.read())
logger.info('I-05-02 秘密鍵ファイルをインポートしました') logger.info('I-05-02 秘密鍵ファイルをインポートしました')
except Exception as e: except Exception as e:
logger.error(f'E-05-01 秘密鍵ファイルのインポートに失敗しました エラー内容:{e}') logger.exception(f'E-05-01 秘密鍵ファイルのインポートに失敗しました エラー内容:{e}')
raise PrivateKeyImportException('E-05-01', EXTENSION_ERROR, e) raise PrivateKeyImportException('E-05-01', EXTENSION_ERROR, e)
# ⑥ 「④」で読み込んだPGP暗号化ファイルを「⑤」でインポートした秘密鍵ファイルで復号する # ⑥ 「④」で読み込んだPGP暗号化ファイルを「⑤」でインポートした秘密鍵ファイルで復号する
@ -109,7 +113,7 @@ def handler(event, context):
decrypt_file = open(PATH_TEMP_DECRYPT_FILE, 'rb') decrypt_file = open(PATH_TEMP_DECRYPT_FILE, 'rb')
logger.info('I-06-02 PGP暗号化ファイルを復号しました') logger.info('I-06-02 PGP暗号化ファイルを復号しました')
except Exception as e: except Exception as e:
logger.error(f'E-06-01 PGP暗号化ファイルの復号化に失敗しました エラー内容{e}') logger.exception(f'E-06-01 PGP暗号化ファイルの復号化に失敗しました エラー内容{e}')
raise DecryptException('E-06-01', EXTENSION_DECRYPT_ERROR, e) raise DecryptException('E-06-01', EXTENSION_DECRYPT_ERROR, e)
# ⑦ 各ファイルをS3に出力する # ⑦ 各ファイルをS3に出力する
@ -126,7 +130,7 @@ def handler(event, context):
decrypt_file.close decrypt_file.close
logger.info('I-07-03 復号化ファイルをS3に出力しました') logger.info('I-07-03 復号化ファイルをS3に出力しました')
except Exception as e: except Exception as e:
logger.error(f'E-07-01 復号化ファイルのS3出力に失敗しました エラー内容{e}') logger.exception(f'E-07-01 復号化ファイルのS3出力に失敗しました エラー内容{e}')
raise FileOutputException('E-07-01', EXTENSION_ERROR, e) raise FileOutputException('E-07-01', EXTENSION_ERROR, e)
# 「④」で読み込んだPGP暗号化ファイルを以下に移動コピー削除する # 「④」で読み込んだPGP暗号化ファイルを以下に移動コピー削除する
@ -136,29 +140,41 @@ def handler(event, context):
'Key': s3_event.file_path 'Key': s3_event.file_path
} }
backup_file_key = f'{s3_event.data_source_name}/{execute_date}/{s3_event.file_name}' backup_file_key = f'{s3_event.data_source_name}/{execute_date}/{s3_event.file_name}'
logger.info(f'I-07-04 PGP暗号化ファイル移動 移動元{s3_event.bucket_name}/{s3_event.file_path} 移動先:{SAP_DATA_BACKUP_BUCKET_NAME}/{backup_file_key}') logger.info(
f'I-07-04 PGP暗号化ファイル移動 移動元{s3_event.bucket_name}/{s3_event.file_path} 移動先:{SAP_DATA_BACKUP_BUCKET_NAME}/{backup_file_key}')
backup_file_obj = s3_resource.Object(SAP_DATA_BACKUP_BUCKET_NAME, backup_file_key) backup_file_obj = s3_resource.Object(SAP_DATA_BACKUP_BUCKET_NAME, backup_file_key)
backup_file_obj.copy(copy_source) backup_file_obj.copy(copy_source)
s3_client.delete_object(Bucket=s3_event.bucket_name, Key=s3_event.file_path) s3_client.delete_object(Bucket=s3_event.bucket_name, Key=s3_event.file_path)
logger.info('I-07-05 PGP暗号化ファイルをバックアップ用バケットに移動しました') logger.info('I-07-05 PGP暗号化ファイルをバックアップ用バケットに移動しました')
except Exception as e: except Exception as e:
logger.error(f'E-07-02 PGP暗号化ファイルのS3出力に失敗しました エラー内容{e}') logger.exception(f'E-07-02 PGP暗号化ファイルのS3出力に失敗しました エラー内容{e}')
raise FileOutputException('E-07-02', EXTENSION_ERROR, e)
# 「⑥」で復号化したファイルをデータ取込用バケットに出力する
try:
copy_source = {
'Bucket': decrypt_bucket_name,
'Key': decrypt_file_key
}
import_file_folder = f'{s3_event.data_source_name}{DIRECTORY_TARGET}'
import_file_key = f'{import_file_folder}{decrypt_file_name}'
logger.info(f'I-07-06 復号化ファイル出力 ファイル名:{decrypt_file_name} 出力先:{s3_event.bucket_name}/{import_file_folder}')
import_file_obj = s3_resource.Object(s3_event.bucket_name, import_file_key)
import_file_obj.copy(copy_source)
logger.info(f'I-07-07 復号化ファイルをS3に出力しました')
except Exception as e:
logger.exception(f'E-07-03 復号化ファイルのS3出力に失敗しました エラー内容{e}')
raise FileOutputException('E-07-02', EXTENSION_ERROR, e) raise FileOutputException('E-07-02', EXTENSION_ERROR, e)
# ⑧ 処理終了ログを出力する # ⑧ 処理終了ログを出力する
logger.info('I-08-01 処理終了 SAPデータ復号処理') logger.info('I-08-01 処理終了 SAPデータ復号処理')
except EncryptFileReadException as e: except EncryptFileReadException as e:
traceback.print_exc()
create_status_file(s3_event, e.extension) create_status_file(s3_event, e.extension)
error_notice(e.id, e.arg) error_notice(e.id, e.arg)
except CustomException as e: except CustomException as e:
traceback.print_exc()
create_status_file(s3_event, e.extension) create_status_file(s3_event, e.extension)
move_encrypt_file(s3_event) move_encrypt_file(s3_event)
error_notice(e.id, e.arg) error_notice(e.id, e.arg)
except Exception as e: except Exception as e:
logger.error(f'E-99 想定外のエラーが発生しました エラー内容:{e}') logger.exception(f'E-99 想定外のエラーが発生しました エラー内容:{e}')
traceback.print_exc()
create_status_file(s3_event, EXTENSION_ERROR) create_status_file(s3_event, EXTENSION_ERROR)
move_encrypt_file(s3_event) move_encrypt_file(s3_event)
error_notice('E-99', e) error_notice('E-99', e)
@ -172,10 +188,10 @@ def create_status_file(s3_event, extension) -> None:
result_error_key = s3_event.data_source_name + DIRECTORY_RECV + result_error_file_name result_error_key = s3_event.data_source_name + DIRECTORY_RECV + result_error_file_name
result_error_obj = s3_resource.Object(s3_event.bucket_name, result_error_key) result_error_obj = s3_resource.Object(s3_event.bucket_name, result_error_key)
result_error_obj.put(Body='') result_error_obj.put(Body='')
logger.error(f'E-ERR-01 recvディレクトリにエラーファイルを作成しました ファイル名{result_error_file_name} 出力先:{s3_event.bucket_name}/{result_error_key}') logger.error(
f'E-ERR-01 recvディレクトリにエラーファイルを作成しました ファイル名{result_error_file_name} 出力先:{s3_event.bucket_name}/{result_error_key}')
except Exception as e: except Exception as e:
logger.error(f'E-96 エラーステータスファイルの作成に失敗しました エラー内容:{e}') logger.exception(f'E-96 エラーステータスファイルの作成に失敗しました エラー内容:{e}')
traceback.print_exc()
return return
@ -191,10 +207,10 @@ def move_encrypt_file(s3_event) -> None:
error_obj = s3_resource.Object(s3_event.bucket_name, error_key) error_obj = s3_resource.Object(s3_event.bucket_name, error_key)
error_obj.copy(copy_source) error_obj.copy(copy_source)
s3_client.delete_object(Bucket=s3_event.bucket_name, Key=s3_event.file_path) s3_client.delete_object(Bucket=s3_event.bucket_name, Key=s3_event.file_path)
logger.error(f'E-ERR-02 recv_errorディレクトリにファイルを移動しました 移動元{s3_event.bucket_name}/{s3_event.file_path} 移動先:{s3_event.bucket_name}/{error_key}') logger.error(
f'E-ERR-02 recv_errorディレクトリにファイルを移動しました 移動元{s3_event.bucket_name}/{s3_event.file_path} 移動先:{s3_event.bucket_name}/{error_key}')
except Exception as e: except Exception as e:
logger.error(f'E-97 PGP暗号化ファイルの移動に失敗しました エラー内容{e}') logger.exception(f'E-97 PGP暗号化ファイルの移動に失敗しました エラー内容{e}')
traceback.print_exc()
return return
@ -210,8 +226,7 @@ def error_notice(error_log_id, exception) -> None:
sns_client.publish(**params) sns_client.publish(**params)
logger.error(f'E-ERR-03 処理異常通知の送信指示をしました 通知先トピック:{NDS_NOTICE_TOPIC}') logger.error(f'E-ERR-03 処理異常通知の送信指示をしました 通知先トピック:{NDS_NOTICE_TOPIC}')
except Exception as e: except Exception as e:
logger.error(f'E-98 処理異常通知の送信指示に失敗しました エラー内容:{e}') logger.exception(f'E-98 処理異常通知の送信指示に失敗しました エラー内容:{e}')
traceback.print_exc()
return return

View File

@ -119,9 +119,10 @@ def lambda_handler(event, context):
raise FileReadException('E-05-02', e) raise FileReadException('E-05-02', e)
logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}') logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}')
mail_title_without_line_break = mail_title.splitlines()[0]
params = { params = {
'TopicArn': MBJ_SAP_NOTICE_TOPIC, 'TopicArn': MBJ_SAP_NOTICE_TOPIC,
'Subject': mail_title.rstrip('\n'), 'Subject': mail_title_without_line_break,
'Message': mail_body 'Message': mail_body
} }
sns_client.publish(**params) sns_client.publish(**params)

View File

@ -171,9 +171,10 @@ def lambda_handler(event, context):
raise FileReadException('E-05-02', e) raise FileReadException('E-05-02', e)
logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}') logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}')
mail_title_without_line_break = mail_title.splitlines()[0]
params = { params = {
'TopicArn': MBJ_SAP_NOTICE_TOPIC, 'TopicArn': MBJ_SAP_NOTICE_TOPIC,
'Subject': mail_title.rstrip('\n'), 'Subject': mail_title_without_line_break,
'Message': mail_body 'Message': mail_body
} }
sns_client.publish(**params) sns_client.publish(**params)

View File

@ -173,9 +173,10 @@ def lambda_handler(event, context):
raise FileReadException('E-05-02', e) raise FileReadException('E-05-02', e)
logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}') logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}')
mail_title_without_line_break = mail_title.splitlines()[0]
params = { params = {
'TopicArn': MBJ_SAP_NOTICE_TOPIC, 'TopicArn': MBJ_SAP_NOTICE_TOPIC,
'Subject': mail_title.rstrip('\n'), 'Subject': mail_title_without_line_break,
'Message': mail_body 'Message': mail_body
} }
sns_client.publish(**params) sns_client.publish(**params)

View File

@ -119,9 +119,10 @@ def lambda_handler(event, context):
raise FileReadException('E-05-02', e) raise FileReadException('E-05-02', e)
logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}') logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}')
mail_title_without_line_break = mail_title.splitlines()[0]
params = { params = {
'TopicArn': MBJ_SAP_NOTICE_TOPIC, 'TopicArn': MBJ_SAP_NOTICE_TOPIC,
'Subject': mail_title.rstrip('\n'), 'Subject': mail_title_without_line_break,
'Message': mail_body 'Message': mail_body
} }
sns_client.publish(**params) sns_client.publish(**params)

View File

@ -171,9 +171,10 @@ def lambda_handler(event, context):
raise FileReadException('E-05-02', e) raise FileReadException('E-05-02', e)
logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}') logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}')
mail_title_without_line_break = mail_title.splitlines()[0]
params = { params = {
'TopicArn': MBJ_SAP_NOTICE_TOPIC, 'TopicArn': MBJ_SAP_NOTICE_TOPIC,
'Subject': mail_title.rstrip('\n'), 'Subject': mail_title_without_line_break,
'Message': mail_body 'Message': mail_body
} }
sns_client.publish(**params) sns_client.publish(**params)

View File

@ -173,9 +173,10 @@ def lambda_handler(event, context):
raise FileReadException('E-05-02', e) raise FileReadException('E-05-02', e)
logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}') logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_SAP_NOTICE_TOPIC}')
mail_title_without_line_break = mail_title.splitlines()[0]
params = { params = {
'TopicArn': MBJ_SAP_NOTICE_TOPIC, 'TopicArn': MBJ_SAP_NOTICE_TOPIC,
'Subject': mail_title.rstrip('\n'), 'Subject': mail_title_without_line_break,
'Message': mail_body 'Message': mail_body
} }
sns_client.publish(**params) sns_client.publish(**params)

View File

@ -1,7 +1,7 @@
2022/06/15 2022/06/03
2022/07/15 2022/07/03
2022/08/15 2022/08/03
2022/09/15 2022/09/03
2022/10/15 2022/10/03
2022/11/15 2022/11/07
2022/12/15 2022/12/03

View File

@ -1,73 +0,0 @@
2022/06/04
2022/06/05
2022/06/11
2022/06/12
2022/06/18
2022/06/19
2022/06/25
2022/06/26
2022/07/02
2022/07/03
2022/07/09
2022/07/10
2022/07/16
2022/07/17
2022/07/18
2022/07/23
2022/07/24
2022/07/30
2022/07/31
2022/08/06
2022/08/07
2022/08/11
2022/08/12
2022/08/13
2022/08/14
2022/08/15
2022/08/16
2022/08/20
2022/08/21
2022/08/27
2022/08/28
2022/09/03
2022/09/04
2022/09/10
2022/09/11
2022/09/17
2022/09/18
2022/09/19
2022/09/23
2022/09/24
2022/09/25
2022/10/01
2022/10/02
2022/10/08
2022/10/09
2022/10/10
2022/10/15
2022/10/16
2022/10/22
2022/10/23
2022/10/29
2022/10/30
2022/11/03
2022/11/05
2022/11/06
2022/11/12
2022/11/13
2022/11/19
2022/11/20
2022/11/23
2022/11/26
2022/11/27
2022/12/03
2022/12/04
2022/12/10
2022/12/11
2022/12/17
2022/12/18
2022/12/24
2022/12/25
2022/12/29
2022/12/30
2022/12/31

View File

@ -0,0 +1,3 @@
{
"check_target_schemas": ["custom01", "custom02", "custom03"]
}

View File

@ -0,0 +1,6 @@
宛先各位
 customスキーマの以下のviewに「SQL SECURITY INVOKER」オプションが指定されておりません。viewを再作成しオプションを指定してください。
  {no_option_views}
 尚、本メールはシステム自動送信ですので、返信できません。
 本件に関する問い合わせは、IT部門ゴザリ様にお願いいたします。

View File

@ -0,0 +1 @@
【MeDaCaシステム通知】view参照制限オプション指定漏れを検出しました

View File

@ -0,0 +1,13 @@
SAP_fin
utf-8
LF
1
18
Value Type,Fiscal Yr,Period,Cost Center,Cost Elem.,Cost Elem.text,Value.Crcy,Offsetting acct.,Off.acc.nam,Name,Doc.Text,Posting Date,Doc.Date,Purch.Doc,DocumentNo,Post.row,RefDocNo,Reversed
value_type,fiscal_yr,period,cost_center,cost_elem,cost_elem_text,value_crcy,offsetting_acct,off_acc_nam_name,name,doc_text,posting_date,doc_date,purch_doc,document_no,post_row,ref_doc_no,reversed
src03a.sapf_costreport
org03a.sapf_costreport
value_crcy

View File

@ -0,0 +1,13 @@
SAP_fin
utf-8
LF
1
18
Fiscal Yr,Period,OrderNo.,Cost Elem.,Cost Elem.text,Value.Crcy,Offsetting acct,Offsetting Account Name,AuxAcctAstmt_1,Name,Document Header Text,Posting Date,Doc.Date,Purchasing Doc.,Document No,Post.Row,RefDocNo.,Reversed
fiscal_yr,period,order_no,cost_elem,cost_elem_text,value_crcy,offsetting_acct,offsetting_account_name,aux_acct_astmt_1,name,document_header_text,posting_date,doc_date,purchasing_doc,document_no,post_row,ref_doc_no,reversed
src03a.sapf_ioreport
org03a.sapf_ioreport
value_crcy

View File

@ -0,0 +1,13 @@
SAP_fin
utf-8
LF
1
25
Billing Type,ConditionType,Distribution Channel,Invoiced Number,Item Number,Invoice Date,Account Number (Sold To),Customer Name,Ship-To,Ship-to (Name),Quantity Invoiced,Extended amount (Invoice Amount),Accural Value (Rebate 1),Accural % (Rebate 1),Accural Value (Rebate 2),Accural % (Rebate 2),Unit Selling Price,Item Code,Item Name,Tax,Order Reason,Reference doc,Sales Order No.,Rejected RSN,Batch Number
billing_type,condition_type,distribution_channel,invoiced_number,item_number,invoice_date,account_number_sold_to,customer_name,ship_to,ship_to_name,quantity_invoiced,extended_amount_invoice_amount,accural_value_rebate_1,accural_percent_rebate_1,accural_value_rebate_2,accural_percent_rebate_2,unit_selling_price,item_code,item_name,tax,order_reason,reference_doc,sales_order_no,rejected_rsn,batch_number
src03b.sapf_invoice
org03b.sapf_invoice
quantity_invoiced,extended_amount_invoice_amount,accural_value_rebate_1,accural_percent_rebate_1,accural_value_rebate_2,accural_percent_rebate_2,unit_selling_price,tax

View File

@ -0,0 +1,11 @@
SAP_fin
utf-8
LF
1
10
WBS Element,Level,Obj.no.,Profit Ctr,Project Definition,Description,COAr,Respons.,Basic start,Basic finish
wbs_element,level,obj_no,profit_ctr,project_definition,description,coar,respons,basic_start,basic_finish
src03a.sapf_wbslist
org03a.sapf_wbslist

View File

@ -0,0 +1,13 @@
SAP_fin
utf-8
LF
1
18
Fiscal Yr,Period,WBS Element,Cost Elem.,Cost Elem.text,Value.Crcy,Offsetting acct,Off.acc.name,AuxAcctAsmnt_1,Name,Doc.Text,Posting Date,Doc.Date,Purchasing Doc.,DocumentNo,Post.Row,RefDocNo,Reversed
fiscal_yr,period,wbs_element,cost_elem,cost_elem_text,value_crcy,offsetting_acct,off_acc_name,aux_acct_asmnt_1,name,doc_text,posting_date,doc_date,purchasing_doc,document_no,post_row,ref_doc_no,reversed
src03a.sapf_wbsreport
org03a.sapf_wbsreport
value_crcy

View File

@ -0,0 +1,7 @@
/* SAPデータ取込:Finance */
CostReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) CostReport.txt
IOReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) IOReport.txt
WBSReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) WBSReport.txt
WBSList_[0-9]{8}_[0-9]{6}\.(TSV|tsv) WBSList.txt
Invoice_[0-9]{8}_[0-9]{6}\.(TSV|tsv) Invoice.txt

View File

@ -0,0 +1,13 @@
SAP_sup
utf-8
LF
1
11
Process Order,Operation / Activity,Yield,UoM,Posting date,Finish execution date,Material,Material description,Confirmation,Conf. Counter,Cancelled Conf.
process_order,operation_activity,yield,uo_m,posting_date,finish_execution_date,material,material_description,confirmation,conf_counter,cancelled_conf
src04.saps_confreport
org04.saps_confreport
yield

View File

@ -0,0 +1,13 @@
SAP_sup
utf-8
LF
1
13
Process Order,Material document,Material document item,Material,Material description,Goods movement,Posting date,Movement type,D/C indicator,Storage location,Batch,Qty,Base Unit of Measure
process_order,material_document,material_document_item,material,material_description,goods_movement,posting_date,movement_type,d_c_indicator,storage_location,batch,qty,base_unit_of_measure
src04.saps_gmreport
org04.saps_gmreport
qty

View File

@ -0,0 +1,13 @@
SAP_sup
utf-8
LF
1
24
Plant,Material,Material Description,Batch,Posting Date,Qty in Unit of Entry,Movement Type,Unit of Entry,Entry on,Material Document Year,Document Date,Total valuated stock,Base Unit of Measure,Quantity,Reference,Purchase Order,Customer,Amount,Amount in LC,Vendor,Item,Material Document,Storage Location,Movement Type Text
plant,material,material_description,batch,posting_date,qty_in_unit_of_entry,movement_type,unit_of_entry,entry_on,material_document_year,document_date,total_valuated_stock,base_unit_of_measure,quantity,reference,purchase_order,customer,amount,amount_in_lc,vendor,item,material_document,storage_location,movement_type_text
src04.saps_grreport
org04.saps_grreport
qty_in_unit_of_entry,total_valuated_stock,quantity,amount

View File

@ -0,0 +1,13 @@
SAP_sup
utf-8
LF
1
24
Plant,Material,Material Description,Batch,Posting Date,Qty in Unit of Entry,Movement Type,Unit of Entry,Entry on,Material Document Year,Document Date,Total valuated stock,Base Unit of Measure,Quantity,Reference,Purchase Order,Customer,Amount,Amount in LC,Vendor,Item,Material Document,Storage Location,Movement Type Text
plant,material,material_description,batch,posting_date,qty_in_unit_of_entry,movement_type,unit_of_entry,entry_on,material_document_year,document_date,total_valuated_stock,base_unit_of_measure,quantity,reference,purchase_order,customer,amount,amount_in_lc,vendor,item,material_document,storage_location,movement_type_text
src04.saps_mlcreport
org04.saps_mlcreport
qty_in_unit_of_entry,total_valuated_stock,quantity,amount

View File

@ -0,0 +1,13 @@
SAP_sup
utf-8
LF
1
12
Purchase Doc,Vendor,Item,Material,Short Text,Document Date,Order Quantity,Order Unit,Net Order Value,Currency,Price Unit,Del Indicator
purchase_doc,vendor,item,material,short_text,document_date,order_quantity,order_unit,net_order_value,currency,price_unit,del_indicator
src04.saps_poreport
org04.saps_poreport
order_quantity,net_order_value

View File

@ -0,0 +1,13 @@
SAP_sup
utf-8
LF
1
24
Plant,Material,Material Description,Batch,Posting Date,Qty in Unit of Entry,Movement Type,Unit of Entry,Entry on,Material Document Year,Document Date,Total valuated stock,Base Unit of Measure,Quantity,Reference,Purchase Order,Customer,Amount,Amount in LC,Vendor,Item,Material Document,Storage Location,Movement Type Text
plant,material,material_description,batch,posting_date,qty_in_unit_of_entry,movement_type,unit_of_entry,entry_on,material_document_year,document_date,total_valuated_stock,base_unit_of_measure,quantity,reference,purchase_order,customer,amount,amount_in_lc,vendor,item,material_document,storage_location,movement_type_text
src04.saps_qareport
org04.saps_qareport
qty_in_unit_of_entry,total_valuated_stock,quantity,amount

View File

@ -0,0 +1,13 @@
SAP_sup
utf-8
LF
1
13
SPL.stock Indic,Material Num.,Material Desc.,Storage Location,Batch Num.,Expired Date,Unrestricted Stock,In Quality Stock,Blocked Stock,Consign Stock,Total Stock Quantity,Sold to,Name
spl_stock_indic,material_num,material_desc,storage_location,batch_num,expired_date,unrestricted_stock,in_quality_stock,blocked_stock,consign_stock,total_stock_quantity,sold_to,name
src04.saps_stocklist
org04.saps_stocklist
StockList_ex.sql
unrestricted_stock,in_quality_stock,blocked_stock,consign_stock,total_stock_quantity

View File

@ -0,0 +1,7 @@
/* 蓄積スキーマ */
/* execute_dateがnullのレコードを抽出し、取込ファイル名のyyyymmdd部分を切り出しセットする */
update src04.saps_stocklist
set
execute_date = STR_TO_DATE(SUBSTRING(file_name,11,8),'%Y%m%d')
where
execute_date is null

View File

@ -0,0 +1,9 @@
/* SAPデータ取込:SupplyChain */
GRReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) GRReport.txt
QAReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) QAReport.txt
MLCReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) MLCReport.txt
MLCReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) MLCReport.txt
POReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) POReport.txt
StockList_[0-9]{8}_[0-9]{6}\.(TSV|tsv) StockList.txt
ConfReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) ConfReport.txt
GMReport_[0-9]{8}_[0-9]{6}\.(TSV|tsv) GMReport.txt