なぜログの出力形式をJSONにするのか
ログを出力形式をどうするか考えたとき、基本的にはJSONを推すことにしている。 その理由を整理してみた。
1. ツールを使ってパースしやすい
TableauやSplunkなどのBIツールを使って解析するのにJSON形式は都合が良いと聞いた。 残念ながら自分はBIツールを使って解析するようなことをしないのでこの恩恵がどの程度のものなのかは分からない。
2. 構造の規則性が視覚で分かるので理解しやすい
1行の文章でつらつらとログメッセージが出てきたときに、ぱっと見て分かった!と自分はならない自信がある。 継続してログを見続けるうちにメッセージの単語の位置を覚えて解析も早くなるみたいな熟練は求めてないので、誰もが分かりやすい形を目指したい。するとキーを見て値が想像しやすいJSONはベストかなと思った。 整理されたフォーマットは他の作業者にも共有しやすいと思う。
3. ログを再利用しやすい
JSONなのでそのままAPIで送受信できる。 ログをパースして必要な情報を取り出し、後続の処理に渡すことも可能。
上記の理由で、自分がログの出力形式を決定できるならJSONを推します。
pytestメモ
Udemyでお世話になっている酒井さんの講座の学習メモ。
テストしたい関数がある。
class Cal(object): def add_num_and_double(self, x, y): if type(x) is not int or type(y) is not int: raise ValueError result = x + y result *= 2 return result
関数をテストするpytestファイルを作成する。
# pytestのファイル名は「test_」から始める # 別ファイルの関数にアクセスするのでimportする import calculation import pytest class TestCal(object): # テストを開始するときだけ実行 @classmethod def setup_class(cls): print('start') cls.cal = calculation.Cal() # テストを終了するときだけ実行 @classmethod def teardown_class(cls): print('end') del cls._cal # テスト関数を実行する度に実行 def setup_method(self, method): print('method={}'.format(method.__name__)) # テスト関数が終了する度に実行 def teardown_method(self, method): print('method={}'.format(method.__name__)) # テスト関数 # 4が返るか確認 def test_add_num_and_double(self): assert self.cal.add_num_and_double(1, 1) == 4 # 例外テスト関数 # 文字列を渡してValueErrorが返るか確認(返らなければエラー) def test_add_num_and_double_raise(self): with pytest.raises(ValueError): self.cal.add_num_and_double('1', '1') # スキップ @pytest.mark.skip(reason='Skip!') def test_add_num_and_double(self): assert self.cal.add_num_and_double(1, 1) == 4 # 条件付きスキップ is_release = True @pytest.mark.skipif(is_release==True, reason='Skip!') def test_add_num_and_double(self): assert self.cal.add_num_and_double(1, 1) == 4
django メモ
下書きの肥やしになっていたdjangoの試した時のメモ。
環境
software | version |
---|---|
django | 2.2.1 |
アプリケーションファイル
- views.py
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
- urls.py
from django.urls import path from . import views urlpatterns = [ urlpatterns = [ path('', # 設定した文字列がURLに表示される views.index, name='index'), # projectフォルダのurls.pyで指定する名前 ] ]
モデルフィールド
リレーション先の特定のフィールドの値を取得する設定に苦戦した。
参考:
Django2.0から必須になったon_deleteの使い方 - Djangoの学習ができるチュートリアルサイトDjangoBrothers
トークン認証
エラー対応
AttributeError
runserver
実行時にエラーが発生した。
ubuntu@ip-10-0-1-180:~/django_rest_framework_test$ python3 manage.py runserver 0:8000 Watching for file changes with StatReloader Performing system checks... Exception in thread django-main-thread: Traceback (most recent call last): File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner self.run() File "/usr/lib/python3.6/threading.py", line 864, in run self._target(*self._args, **self._kwargs) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/utils/autoreload.py", line 54, in wrapper fn(*args, **kwargs) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 117, in inner_run self.check(display_num_errors=True) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/base.py", line 390, in check include_deployment_checks=include_deployment_checks, File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/base.py", line 377, in _run_checks return checks.run_checks(**kwargs) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/checks/registry.py", line 72, in run_checks new_errors = check(app_configs=app_configs) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/checks/urls.py", line 13, in check_url_config return check_resolver(resolver) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/checks/urls.py", line 23, in check_resolver return check_method() File "/home/ubuntu/.local/lib/python3.6/site-packages/django/urls/resolvers.py", line 398, in check for pattern in self.url_patterns: File "/home/ubuntu/.local/lib/python3.6/site-packages/django/utils/functional.py", line 80, in __get__ res = instance.__dict__[self.name] = self.func(instance) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/urls/resolvers.py", line 579, in url_patterns patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/utils/functional.py", line 80, in __get__ res = instance.__dict__[self.name] = self.func(instance) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/urls/resolvers.py", line 572, in urlconf_module return import_module(self.urlconf_name) File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 994, in _gcd_import File "<frozen importlib._bootstrap>", line 971, in _find_and_load File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 665, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 678, in exec_module File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed File "/home/ubuntu/django_rest_framework_test/django_rest_framework_test/urls.py", line 20, in <module> from petstore.urls import router as petstore_router File "/home/ubuntu/django_rest_framework_test/petstore/urls.py", line 4, in <module> from .views import UserViewSet, PetViewSet File "/home/ubuntu/django_rest_framework_test/petstore/views.py", line 10, in <module> class UserViewSet(viewsets.ModelViewSet): File "/home/ubuntu/django_rest_framework_test/petstore/views.py", line 11, in UserViewSet queryset = User.Objects.all() AttributeError: type object 'User' has no attribute 'Objects'
解決に必要な情報が末尾3行にある。
File "/home/ubuntu/django_rest_framework_test/petstore/views.py", line 11, in UserViewSet queryset = User.Objects.all() AttributeError: type object 'User' has no attribute 'Objects'
view.py
のqueryset = User.Objects.all()
を行ったが、オブジェクトUser
にObjects
は無いよ、という意味。
Objects
がobjects
のタイポだったため修正して解決した。
You are trying to add a non-nullable field
複数のモデル間でリレーションを設定するため、models.py
に新しいフィールドを追加。
migrate を実行するとYou are trying to add a non-nullable field
のエラーを出力した。
ubuntu@ip-10-0-1-180:~/django_rest_framework_test$ python3 manage.py makemigrations No changes detected ubuntu@ip-10-0-1-180:~/django_rest_framework_test$ python3 manage.py migrate Operations to perform: Apply all migrations: admin, auth, blog, contenttypes, petstore, sessions Running migrations: No migrations to apply. ubuntu@ip-10-0-1-180:~/django_rest_framework_test$ python3 manage.py makemigrations You are trying to add a non-nullable field 'category' to pet without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py Select an option: Please select a valid option:
新しいフィールドの追加が既に登録したデータにも行われるがフィールドが空を許可していないため、何か値を入れるためのアクションしてね~という内容。
対応の一つとして、models に null=True
を設定し空の場合はnullが入るようにすることで回避できる。
class Pet(models.Model): category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
bad_value, referenced_table_name, referenced_column_name
リレーションを設定した項目が親のid
の値を保持していたので、別のフィールド値を持たせたいと思い設定を変更した。
migrate 時にエラーが出力された。
ubuntu@ip-10-0-1-180:~/django_rest_framework_test$ python3 manage.py migrate Operations to perform: Apply all migrations: admin, auth, blog, contenttypes, petstore, sessions Running migrations: Applying petstore.0004_auto_20190514_1848...Traceback (most recent call last): File "manage.py", line 21, in <module> main() File "manage.py", line 17, in main execute_from_command_line(sys.argv) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line utility.execute() File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/__init__.py", line 375, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/base.py", line 323, in run_from_argv self.execute(*args, **cmd_options) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/base.py", line 364, in execute output = self.handle(*args, **options) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/base.py", line 83, in wrapped res = handle_func(*args, **kwargs) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/core/management/commands/migrate.py", line 234, in handle fake_initial=fake_initial, File "/home/ubuntu/.local/lib/python3.6/site-packages/django/db/migrations/executor.py", line 117, in migrate state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/db/migrations/executor.py", line 147, in _migrate_all_forwards state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial) File "/home/ubuntu/.local/lib/python3.6/site-packages/django/db/migrations/executor.py", line 247, in apply_migration migration_recorded = True File "/home/ubuntu/.local/lib/python3.6/site-packages/django/db/backends/sqlite3/schema.py", line 34, in __exit__ self.connection.check_constraints() File "/home/ubuntu/.local/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 318, in check_constraints bad_value, referenced_table_name, referenced_column_name django.db.utils.IntegrityError: The row in table 'petstore_pet' with primary key '1' has an invalid foreign key: petstore_pet.category contains a value '2' that does not have a corresponding value in petstore_category.name. ubuntu@ip-10-0-1-180:~/django_rest_framework_test$
フィールドが対応しない形の値(変更前の値)を保持しているため、新しく保持しようとした値が入れられない。 エラーとなる値を削除することで回避可能。
シェルスクリプトをSSMのドキュメントに登録して実行する
目的
Lambdaを使ってEC2上でコマンド実行したい
実現案
Lambda --> SSM --> EC2
SSMが実行するアクションを「ドキュメント」として定義できる。
ドキュメントにはいくつかのタイプがあり、コマンドを使用できるRun Command
を使えば実現できそう。
やってみた
SSMのメニューでドキュメント
を選択後、Create command or session
を選択。
項目 | 値 |
---|---|
名前 | 任意の名前 |
ターゲットタイプ | /AWS::EC2::Instance |
ドキュメントタイプ | コマンドドキュメント |
コンテンツ | YAML |
内容は以下で設定。
--- schemaVersion: "2.2" description: "Command Document Example YAML Template" mainSteps: - action: "aws:runShellScript" name: "hostname" inputs: runCommand: - "hostname" - "ls -l /etc"
hostname
やls
を試す内容です。
以上の内容で作成する。
自己所有で作成したドキュメントが確認できました。
作成したドキュメントを実行すると以下の結果が得られました。
ip-10-0-1-188 total 1508 -rw-r--r-- 1 root root 5090 Feb 25 2016 DIR_COLORS -rw-r--r-- 1 root root 5725 Feb 25 2016 DIR_COLORS.256color -rw-r--r-- 1 root root 4669 Feb 25 2016 DIR_COLORS.lightbgcolor -rw-r--r-- 1 root root 94 Aug 17 2017 GREP_COLORS drwxr-xr-x 3 root root 4096 Nov 16 2018 NetworkManager drwxr-xr-x 4 root root 4096 Nov 16 2018 X11 drwxr-xr-x 4 root root 4096 Nov 16 2018 acpi -rw-r--r-- 1 root root 44 Jan 27 15:37 adjtime -rw-r--r-- 1 root root 1512 Jan 12 2010 aliases -rw-r----- 1 root smmsp 12288 Nov 16 2018 aliases.db drwxr-xr-x 2 root root 12288 Jan 20 11:06 alternatives drwxr-xr-x 3 root root 4096 Nov 16 2018 amazon -rw------- 1 root root 541 Sep 28 2016 anacrontab -rw-r--r-- 1 root root 148 Jan 6 2012 asound.conf -rw-r--r-- 1 root root 1 Aug 16 2016 at.deny drwxr-x--- 3 root root 4096 Nov 16 2018 audisp drwxr-x--- 3 root root 4096 Nov 16 2018 audit drwxr-xr-x 2 root root 4096 Jan 20 10:35 bash_completion.d -rw-r--r-- 1 root root 2681 Sep 10 2014 bashrc drwxr-xr-x 2 root root 4096 Jan 20 10:28 blkid -rw-r--r-- 1 root root 905 Feb 28 2014 cgconfig.conf -rw-r--r-- 1 root root 234 Feb 28 2014 cgrules.conf -rw-r--r-- 1 root root 131 Feb 28 2014 cgsnapshot_blacklist.conf drwxr-xr-x 2 root root 4096 Mar 17 2015 chkconfig.d drwxr-xr-x 4 root root 4096 Nov 16 2018 cloud drwxr-xr-x 2 root root 4096 Jan 20 10:48 containerd drwxr-xr-x 2 root root 4096 Jan 27 13:52 cron.d drwxr-xr-x 2 root root 4096 Nov 16 2018 cron.daily -rw------- 1 root root 0 Sep 28 2016 cron.deny drwxr-xr-x 2 root root 4096 Jan 6 2012 cron.hourly drwxr-xr-x 2 root root 4096 Jan 6 2012 cron.monthly drwxr-xr-x 2 root root 4096 Jan 6 2012 cron.weekly -rw-r--r-- 1 root root 457 Jan 6 2012 crontab -rw-r--r-- 1 root root 1602 Sep 10 2014 csh.cshrc -rw-r--r-- 1 root root 794 Sep 10 2014 csh.login drwxr-xr-x 4 root root 4096 Jan 20 10:27 dbus-1 drwxr-xr-x 2 root root 4096 Jan 20 10:27 default drwxr-xr-x 2 root root 4096 Nov 16 2018 depmod.d drwxr-x--- 4 root root 4096 Nov 16 2018 dhcp drwxr-xr-x 2 root root 4096 Jan 20 10:48 docker -rw-r--r-- 1 root root 519 May 6 2019 dracut.conf drwxr-xr-x 2 root root 4096 Jan 20 10:27 dracut.conf.d -rw-rw-r-- 1 root disk 0 Jan 5 2012 dumpdates -rw-r--r-- 1 root root 119 Oct 1 2018 e2fsck.conf ---Output truncated---
hostname
コマンドとls
コマンドの結果が出てますね。
S3に出力するオプションを有効にすると、出力結果のファイルが作成されていました。
Python の if not と or を組み合わせた時は条件に注意
Pythonで以下のようなif not 条件1 or 条件2
を実行する。
def sleep_in(weekday, vacation): if not weekday or vacation: return True else: return False
すると以下のような結果となる。
weekday | vacation | 実行結果 |
---|---|---|
True | True | True |
True | False | False |
False | True | True |
False | False | True |
ここでvacation
がnot
の判定になっていないことに疑問を感じた人向けの内容となるが、
これはnot
がweekday
のみに有効になっているためである。
判定を分解すると以下のようになる。
not 条件1
条件2
もし条件2
もnot
で判定させたければ、if not 条件1 or not 条件2
というように都度not
を付けるようにする。
Elasticache比較
Elasticacheとは
インメモリデータベースのサービス。
インメモリデータベースはデータをメモリ上で持ち、ディスクにアクセスする必要性を除くことによって、最小限の応答時間を達成するように設計されている。
すべてのデータはメインメモリにのみ保存および管理されているので、処理やサーバー障害によって失われてしまうリスクがあるが、すべてのオペレーションをログに保存したりスナップショットを取得したりすることで、データを存続することができる。
ElasticacheではKey-Value型のオープンソースのNoSQLデータベースであるRedis
と、Key-Value型のオープンソースのキャッシュシステムであるmemcached
を利用出来る。
Redisの特徴
- リモートディクショナリサーバーの略
- 高速に値をRead/WriteできるNoSQL
- データベース、キャッシュ、メッセージブローカー、およびキューとして使用
- 全てのデータ操作は排他的
- メモリ内Key-Valueデータストア (データアクセスが高速)
- メモリを使い果たしたら特定のルールに従って削除する
- それでもメモリを確保できないときは全ての書込みをエラーにする
- MASTER-SLAVEのレプリケーション構築が可能 (SLAVEは複数設定可能)
- レプリケーションは非同期
- 複数の操作を1回で実行できる
- MULTIでトランザクション開始
- 全て実行(EXEC)or全て未実行(DISCARD)
- 幅広いユースケースに対して効果的
- keyに対して有効期限を設定できる(SessionID, OneTimeToken)
- ディスクI/Oを無効化できる
- イベント駆動アーキテクチャ
- シングルスレッドで動作 (複数の処理を並列で行えない)
- CPUの1コアのみを使用 (複数コアを利用する場合は複数台のRedisを立ち上げ推奨)
- 5つのデータ型が使用可能
- 文字列型
- リスト型
- セット型
- ソート済みセット型
- ハッシュ型
- プライマリ/レプリカアーキテクチャ
ユースケース
- キャッシュ
- チャット、メッセージング、キュー
- ゲームのリーダーボード
- セッションストア
- リッチメディアストリーミング
- 地理空間
- Machine Learning
- リアルタイム分析
Memcachedの特徴
- シンプル
- キャッシュやセッションストアとして役立ちます
- 分散型
- 新しいノードを追加することにより簡単にスケールアウトできます
- マルチスレッド
- 特定のノードにおいて複数のコアを利用可能
- ディスクI/Oが発生しない
ユースケース
- キャッシュ
- 永続性が重要ではないセッションストア
- ウェブ
- モバイルアプリケーション
- ゲーム
- アドテック
- eコマース
Redis 対 Memcached
どちらも持つ特徴
- インメモリ
- オープンソースデータストア
Redisのみ
- データ構造のサポート
- String に加えて、List、Set、Sorted Set、Hash、Bit Array、HyperLogLog をサポートしています。アプリケーションは、これらの柔軟なデータ構造を使用して、さまざまなユースケースをサポートできます。たとえば、Redis の Sorted Set を使用すると、ランク別にソートされたプレイヤーのリストを維持したゲームの順位表を簡単に実装できます。
- スナップショット
- レプリケーション
- Redis プライマリの複数のレプリカを作成できます。これにより、データベースの読み取りを拡張し、可用性の高いクラスターを設定できます。
- トランザクション
- 独立したアトミック操作として一連のコマンドを実行できるトランザクションをサポートしています。
- Pub/Sub
- Lua スクリプト
- 地理空間のサポート
- 大規模なリアルタイムの地理空間データを処理するための専用コマンドがあります。2 つの要素 (人や場所など) 間の距離を見つけたり、ある点から所定の距離内にあるすべての要素を見つけるなどの操作を実行できます。
Memcachedのみ
- マルチスレッドであるため複数の処理コアを使用できる
ask-sdk V2 for node.js 開発メモ
ask-sdk も node.js も分からない状態からスキル開発を行いました。その時のメモです。
誰かの役に立つかもしれないのでまとめ方雑ですが載せます。
node.js
strict mode
コードの先頭に"use strict";
と記述があるかもしれません。
これは通常よりも厳しくコードのチェックを行う宣言で、エラーではないけど落とし穴になりそうな内容をエラーとして扱います。
具体的には以下のように扱いが変わります。
- 従来は受け入れていた一部のミスをエラーへ変換
- 偶発的にグローバル変数を作成できないようにします
- 代入文で暗黙的に失敗せずに例外が発生するようにします
- 削除できないプロパティを削除しようとするとエラーが発生します
- 変数の使用の単純化
- with を禁止します
- eval は新しい変数を周囲のスコープに広めません
- 単純名の削除を禁止します
- eval および arguments の単純化
- eval および arguments という名前に対して言語構文でのバインドや代入を不可にします
- 内部で作成された arguments オブジェクトのプロパティがエイリアスになりません
- rguments.callee をサポートしません
- JavaScript の "セキュア化"
- this として関数に渡された値をオブジェクトへボクシングしません
- ECMAScript の一般的な実装である拡張を通して JavaScript のスタックを "渡り歩く" ことができません
- arguments は対応する関数の呼び出し時の変数にアクセスできません
- 将来の ECMAScript への準備
スキルに対してもこの機能を有効にするかどうかの判断が必要になります。
参考 - https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Strict_mode - https://www.sejuku.net/blog/58342
var
変数の宣言。関数のスコープで定義する。
最近(ES6)では殆ど使ってないっぽい。
基本は後述するlet
やconst
を使う。
let
変数の宣言。ブロックのスコープで定義する。 代入のし直しが可能。 再宣言ができない。エラーとなる。
const
変数の宣言。ブロックのスコープで定義する。 再代入による変更はできず、再宣言もできない。
startsWith
文字列が特定の文字列で始まるかどうかを判断する。 (英文字の)大文字・小文字を区別する。
parseInt
文字列を整数に変換する。
parseInt()関数は第1引数を文字列に変換し、解析したうえで、整数またはNaNを返します。戻り値は、NaNでなければ、第1引数のstringを第2引数radixの基数によって示す10進数の整数です。たとえば、radixが10なら10進数、8は8進数、16であれば16進数による変換を意味します。10以上の基数については、9より大きい数字はアルファベットで示されます。たとえば、16進数(基数16)ではAからFが用いられます。
indexOf
indexOf() メソッドは、呼び出す String オブジェクト中で、 fromIndex から検索を始め、指定された値が最初に現れたインデックスを返します。値が見つからない場合は -1 を返します。
例)
var paragraph = 'The quick brown fox jumps over the lazy dog. If the dog barked, was it really lazy?'; var searchTerm = 'dog'; var indexOfFirst = paragraph.indexOf(searchTerm); console.log('The index of the first "' + searchTerm + '" from the beginning is ' + indexOfFirst); // expected output: "The index of the first "dog" from the beginning is 40" console.log('The index of the 2nd "' + searchTerm + '" is ' + paragraph.indexOf(searchTerm, (indexOfFirst + 1))); // expected output: "The index of the 2nd "dog" is 52"
IsMatch
値がMapオブジェクトとして分類されているかどうかを確認します。 https://www.npmjs.com/package/lodash.ismatch
Array.prototype.join()
join() メソッドは、配列 (または配列風オブジェクト) の全要素を順に連結した文字列を新たに作成して返します。区切り文字はカンマ、または指定された文字列です。
var elements = ['Fire', 'Wind', 'Rain']; console.log(elements.join()); // expected output: "Fire,Wind,Rain" console.log(elements.join('')); // expected output: "FireWindRain" console.log(elements.join('-')); // expected output: "Fire-Wind-Rain"
... (ドット3つ)
ドットが3つ連続しているものをスプレッド構文と呼ぶ。 順番に値が取り出されて、個数分の要素が該当部分に入るような配列を作成できる。
var ary = [0, "A", false]; var str = "あいう"; var connectedAry = [...ary, ...str]; console.log(connectedAry); /* [0, "A", false, "あ", "い", "う"] */
サンプル
audioData = [ { title: 'Episode 139', url: 'test1.url', }, { title: 'Episode 140', url: 'test2.url', }, ]; const ary1 = audioData; console.log(ary1); // output // [ { title: 'Episode 139', url: 'test1.url' }, // { title: 'Episode 140', url: 'test2.url' } ] const ary2 = [...audioData]; console.log(ary2); // output // [ { title: 'Episode 139', url: 'test1.url' }, // { title: 'Episode 140', url: 'test2.url' } ] console.log(audioData.keys()); // output // Object [Array Iterator] {} const ary3 = [...audioData.keys()]; console.log(ary3); // output // [ 0, 1 ]
Array.from()
配列型 (array-like) や反復型 (iterable) オブジェクトから、新しい Array インスタンスを生成します。
const str = "あいう"; const ary = Array.from(str); console.log(ary); // output // [ 'あ', 'い', 'う' ]
サンプル
const aryfrom1 = [...Array("A","B","C")]; console.log(aryfrom1); // output // [ 'A', 'B', 'C' ] const aryfrom2 = [...Array("A","B","C")].length; console.log(aryfrom2); // output // 3 const aryfrom3 = [...Array(3)] console.log(aryfrom3); // output // [ null, null, null ] const aryfrom4 = [...Array("A","B","C")].length; const aryfrom5 = [...Array(aryfrom4).keys()]; console.log(aryfrom5); // output // [ 0, 1, 2 ]
async
Node.jsのasyncは非同期処理を可読性の高いコードで実装できます。
非同期処理とはページが更新された際などに、更新前と更新後を比較して足りない部分だけをデータ通信する処理のことです。 この非同期処理の事をフロントエンド側の処理ではAjaxと呼ぶ事もあります。
スキルの場合はasync
を handler に設定して、await
を handler 内の DynamoDB 等と通信する部分に設定すると非同期処理ができる。
== (等価演算子)
== は、文字列と数値の比較の場合、文字列を数値に変換してくれる。
数値と文字列を比較するとき、文字列は数値に変換されます。JavaScript は文字列の数値リテラルを Number 型の数値に変換しようと試みます。最初に、その文字列の数値リテラルから数学的な値を引き出します。次に、最も近い Number 型の値にこの値を丸めます。
=== (厳密等価演算子)
オペランド同士が、型を変換することなく厳密に等しいならば真を返します。
Object.prototype.hasOwnProperty()
オブジェクトが指定されたプロパティを持っているかどうかを示す真偽値を返します。
下記の例では、propという名前のプロパティをオブジェクトが含むか否かを確認しています。
o = new Object(); o.prop = 'exists'; function changeO() { o.newprop = o.prop; delete o.prop; } o.hasOwnProperty('prop'); // returns true changeO(); o.hasOwnProperty('prop'); // returns false
Object.getOwnPropertyNames()
与えられたオブジェクトで発見されたすべてのプロパティ (列挙可能・不可能を問わず) の配列を返します。
const object1 = { a: 1, b: 2, c: 3 }; console.log(Object.getOwnPropertyNames(object1)); // expected output: Array ["a", "b", "c"]
Array.length
length プロパティは配列の要素数を取得します。これは符号なし32bitの整数で、常に配列内インデックスの最大値よりも大きな数値になっています。
サンプル
var clothing = ['shoes', 'shirts', 'socks', 'sweaters']; console.log(clothing.length); // expected output: 4
var clothing = [{PrefectureName: "北海道", Romanization: "hokkaido", PrefecturalOfficeLocation: "札幌", PrefectureFlower: "ハマナス", PrefectureOrder: 1 }, {PrefectureName: "鹿児島県", Romanization: "kagoshima", PrefecturalOfficeLocation: "鹿児島", PrefectureFlower: "ミヤマキリシマ", PrefectureOrder: 46 }, {PrefectureName: "沖縄県", Romanization: "okinawa", PrefecturalOfficeLocation: "那覇", PrefectureFlower: "デイゴ", PrefectureOrder: 47 } ]; console.log(clothing.length); // expected output: 3
Array.keys()
keys() メソッドは、配列の各インデックスのキーを含む、新しい Array Iterator オブジェクトを返します。
var array1 = ['a', 'b', 'c']; var iterator = array1.keys(); for (let key of iterator) { console.log(key); } // output // > 0 // > 1 // > 2
Object.keys()
Object.keys() メソッドは、指定されたオブジェクトが持つ names プロパティの配列を、通常のループで取得するのと同じ順序で返します。
const object1 = { a: 'somestring', b: 42, c: false }; console.log(Object.keys(object1)); // expected output: Array ["a", "b", "c"]
for...of
オブジェクトの一覧をループで出力。
for...of 文は、iterableオブジェクトに対して反復的な処理をするループを作成します
let iterable = [10, 20, 30]; for (let value of iterable) { value += 1; console.log(value); } // output // 11 // 21 // 31 let hoge = [{foo:10}, {bar:20}, {baz:30}]; for (let value of hoge) { console.log(value); } // output // [object Object] // [object Object] // [object Object] for (let value of hoge.keys()) { console.log(value); } // output // 0 // 1 // 2
オブジェクトとプロパティ
以下はmyCar
というオブジェクトの生成です。
let myCar = new Object();
以下のようにしてプロパティを定義することが可能です。
myCar.make = 'Ford'; myCar.model = 'Mustang'; myCar.year = 1969; console.log(myCar) // output // { // make: "Ford" , // model: "Mustang" , // year: 1969 // }
未定義のプロパティはundefined
になります。
console.log(myCar.color) // output // undefined
オブジェクトのプロパティの名前には、あらゆる有効な JavaScript 文字列(空文字列を含む)か、文字列に変換できるものが使用できます。
しかしながら、JavaScript 識別子として有効ではないプロパティ名(例えば空白やダッシュを含んでいたり、数字で始まったりするプロパティ名)には、ブラケット(角括弧)表記法でのみアクセスできます。
この表記法はプロパティ名を動的に決める場合(プロパティ名が実行時に決まる場合)に便利です。
モジュール
moment
日時を取得するために使用。 https://momentjs.com/
ask-sdk V2
canHandle
Alexa SDK V2の、リクエストハンドラーとエラーハンドラーのインターフェースのメソッド。
SDKによって呼び出され、指定されたハンドラーが受け取ったリクエストやエラーを処理できるかどうかを判断します。
ex)
HelloWorldIntentHandler
の処理を、HelloWorldIntent
を受け取った時に呼び出す記述。
const HelloWorldIntentHandler = { canHandle(handlerInput) { const request = handlerInput.requestEnvelope.request; return request.type === 'IntentRequest' && request.intent.name === 'HelloWorldIntent'; }, handle(handlerInput) { const speechText = 'こんにちは'; return handlerInput.responseBuilder .speak(speechText) .withSimpleCard('Hello World', speechText) .getResponse(); } };
Playbackディレクティブ
PlaybackStopped
「アレクサ」と声をかけ、アレクサが待ち受け状態になったときに送信される。
PlaybackController
PlaybackControllerリクエストに応答する場合、AudioPlayerディレクティブでしか応答できません。
応答には、outputSpeech、card、repromptなどの標準のプロパティはいずれも含めることができません。
サポートされていないプロパティを含む応答を送信すると、エラーが発生します。
(次へ
、前へ
のボタンを押した動作には発話させられずシンプルカードの表示もできない)
LaunchIntent
スキルの呼び出し時にパラメーターも付与していたときは、LaunchIntentの処理をスキップして後続のパラメーターを処理するHandlerを呼び出す。
LaunchIntentに必要な処理を定義していると、スキップ時は意図しない状態でHandlerを呼び出すこともあるので注意。
Launch時にshouldEndSessionフラグをtrueにするとError retrieving device rendering.
が返される?
https://github.com/alexa/skill-sample-nodejs-audio-player/issues/144
カスタムインテント
標準ビルトインインテントと同じフレーズを登録可能。
標準ビルトインインテントと競合するフレーズを登録していた場合、優先して処理される。
標準ビルトインインテントの方が幅広い範囲を網羅できるため、競合させない方が良い。
AudioPlayerを実行した後は受付できない。
標準ビルトインインテント
カスタムインテントより処理の優先度が低い。 インテントによってフレーズの拡張ができるもの、できないものがある。
- できるもの
- できないもの
AudioPlayerインタフェースを有効にすると、ビルトインインテントはインテントに登録していなくても 受け付ける。
インテントのサンプル発話に登録できない言葉
仕様で登録できない言葉がある。
終了
その他終了に近しい表現
Dialogモデル
slot値を満たせなかったときの発話をskill側で定義して対応可能。 MP3は流せない。
addRequestInterceptors
前処理(RequestInterceptor)を追加します。
LaunchRequestHandlerのcanHandleメソッド ⇒ 前処理 ⇒ LaunchRequestHandlerのhandleメソッド の順で処理。
インテント処理も呼び出すたびに実行される。
addResponseInterceptors
後処理(ResponseInterceptor)を追加します。 handleメソッド ⇒ 後処理 ⇒ 返事。 エラーインテントに振り分けられた場合は実行されていない様子。 正常時はインテント処理を呼び出すたびに実行される。
SSML audioタグ
- 5つ以上のaudioタグを埋め込んだlambda関数を実行するとスキルの起動に失敗する。
- 音声再生が240秒を超える音声を再生するとスキルが正しく応答しないというエラーを報告してスキルが終了する。(オーディオタグの再生時に音声の長さを把握しているように見受けられる)
- 音声再生中でもリプロンプトの応対可能。
SSML breakタグ
連続して複数使用できる?
スキルの待ち受け時間
- アレクサはユーザからの返答(リプロンプト)を7~8秒待つ
- その後、リプロンプト処理を行い再度7~8秒待つ
- 応答がなければスキルを終了する
- リプロンプト回数は変更できない
アレクサ(スキル) からユーザーへの返答時間
8秒。
プログレッシブ応答により、応答に使える総時間が変わるわけではありません。ユーザーがスキルを呼び出すと、スキルは約8秒以内に完全な応答を返す必要があります。スキルは完全な応答だけでなく、プログレッシブ応答の処理についてもこの時間内に終了する必要があります。 プログレッシブ応答の送信手順
https://developer.amazon.com/ja/docs/custom-skills/send-the-user-a-progressive-response.html
attributes の管理
async/await を使ったattributesの操作を行うときは処理の順序を意識しないと変数が空のままなので注意。
永続アトリビュートのキー
DynamoDB側ではAlexaデバイスにログインしているuserIdをプライマリパーティションキーとする。
ダイアログモード
Dialogインターフェースは、スキルとユーザーとの間のマルチターンの会話を管理するためのディレクティブを提供します。ユーザーのリクエストに応えるために必要な情報をユーザーにたずねるときに使用できます
AudioPlayerインタフェース
metadata の設定
- addAudioPlayerPlayDirective は6つ目の引数に metadata パラメータを付与することで、画面付き端末にタイトル情報や画像を表示できる。
- SDKのバージョンが古いと metadata を取得しない。(Lambda のfactテンプレートで設定するSDKは古いバージョンのため注意すること。自分はこれでハマった。)
- 画像の表示はキャッシュを利用して行う。キャッシュはtokenの値に対応しており、同じtokenを使いまわすと意図しない画像を表示する可能性がある。
特定のインテントに該当しないインテントの処理
ErrorHandlerで処理する。
リクエストタイプ
PlaybackController.PlayCommandIssued
ユーザーが再生を開始また再開するためにインテントで「再生」または「再開」ボタンを使用した場合に送信されます。
System.ExceptionEncountered
PlaybackControllerリクエストに対する応答が原因でエラーが発生した場合、System.ExceptionEncounteredリクエストがスキルに送信されます。応答に含まれるディレクティブはすべて無視されます。
発話が一致しない場合にフォールバックを提供する
ユーザーの音声入力がスキルの他のインテントとまったく一致しない場合、AMAZON.FallbackIntent(英語を使用するロケールとドイツ語で利用可能)がトリガーされます。
https://developer.amazon.com/ja/docs/custom-skills/standard-built-in-intents.html#fallback
現状は日本では使用できないため、登録したサンプルと似ない発話もカスタムインテントで処理されることは仕様。
エラー
Unsupported Directive
AudioPlayer is currently an unsupported namespace. Check the device log for more information.
AlexaシミュレータでAudioPlayerによる音声再生をしようとすると発生。 AlexaシミュレータがAudioPlayerインタフェースの再生に対応していないため実機のechoを使う必要がある。
RequestHandlerChain not found!
条件に合致するHandlerが無いと発生する。 スキルのI/O入力とLambdaで定義しているHandlerの条件を確認し、意図しないパラメーターを保持していないか確認する。
Cannot read property 'trim' of undefined
return
で返す値に未定義のものがあると発生する。
reprompt
の定義忘れとかありませんか?
Task timed out after 3.00 seconds
Lambdaがタイムアウトしている。 Lambdaのタイムアウト時間を調整してあげよう。
Unable to find a suitable request handler.
不明。
DynamoDB
DynamoDB.DocumentClient
SDK。
DynamoDBはテーブル作成時にプライマリキー
として以下の2つのキーを登録できる。
- パーティションキー
- ソートキー
DynamoDBからGetを行うときはプライマリキー
を指定する。
パーティションキー
のみを定義した場合はパーティションキー
のみの指定で良いが、
ソートキー
も定義した場合は、パーティションキー
およびソートキー
の指定が必要になる。