ふるてつのぶろぐ

福岡在住のエンジニアです。

写真提供:福岡市

Angular 9 で Web アプリを作ろう - カスタムバリデーション

今日すること

こんにちは、ふるてつです。

今日はカスタムバリデーションについて書きます。
Angular標準のバリデーションは1つの単項目しかチェックできません。 複数の項目にまたがったチェックをしたい場合はカスタムバリデーションを作ります。

例えば画面上の「在庫数」と「購入数」を比較し「在庫数より購入数が上回っている場合」に在庫不足エラーとする。
そんな感じのバリデーションが簡単に書けるので便利です。

今回その「在庫数」と「購入数」を比較するバリデーションを作ってみました。
少しつきなみかもしれませんが、今日はそのお話をします😎

日本語リファレンスの「カスタムバリデータ」~「クロスフィールドバリデーション」を参考にして書きました。
https://angular.jp/guide/form-validation

f:id:tetsufuru:20200304004000p:plain

カスタムバリデーションを書く場所

まずカスタムバリデーションを追加する場所ですが、基本的にはcomponentts中に書きます。
わたしの場合は、リアクティブフォームを使っていますので、下記のようにformBuilder.group()の中になります。
バリデーションは「PurchaseQuantityStockQuantityValidator」という名前です。

registeringForm = this.formBuilder.group(
  {
    productCode: this.productCode,
    productName: this.productName,
 ~ 中略 ~
    validatorLocale: this.validatorLocale
  },
  {
    // ↓ ここです。
    // ↓ formBuilder.group()の validators に追加します。
    validators: [PurchaseQuantityStockQuantityValidator]
    // ↑ ここです。
  }
);

カスタムバリデーションの内容

バリデーションの内容は下記になりました。
長くないので全文を掲載しました。
「在庫数」と「購入数」以外に「ロケール」も必要になったので、追加してます。
(カンマ区切りになっている在庫数や購入数を数値に戻すときに必要になりました)
そしてこのバリデーションを使うフォームにも「ロケール」が必要になりますねー。

import { FormattedNumberPipe } from 'src/app/core/pipes/formatted-number.pipe';

import { FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';

const PRODUCT_STOCK_QUANTITY = 'productStockQuantity';
const PRODUCT_PURCHASE_QUANTITY = 'productPurchaseQuantity';
const VALIDATOR_LOCALE = 'validatorLocale';

export const PurchaseQuantityStockQuantityValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
  // フォームから必要なコントロールの値を取得する。
  // ↓「在庫数」
  const productStockQuantity: string = control.get(PRODUCT_STOCK_QUANTITY).value;
  // ↓「購入数」
  const productPurchaseQuantity: string = control.get(PRODUCT_PURCHASE_QUANTITY).value;
  // ↓「ロケール」
  const validatorLocale: string = control.get(VALIDATOR_LOCALE).value;

  // どちらかが空の場合は null を返します(エラーなしとします)。  
  if (!productStockQuantity) {
    return;
  }
  if (!productPurchaseQuantity) {
    return;
  }
  
  // 自作のパイプでカンマ区切りの値を数値に戻しています。  
  const formattedNumberPipe: FormattedNumberPipe = new FormattedNumberPipe();
  const numProductStockQuantity = Number(formattedNumberPipe.parse(productStockQuantity, validatorLocale));
  const numProductPurchaseQuantity = Number(formattedNumberPipe.parse(productPurchaseQuantity, validatorLocale));

  //  在庫数が多い場合は正常なので null を返します(エラーなし)。
  if (numProductPurchaseQuantity <= numProductStockQuantity) {
    return;
  }

  // ↓ ここから下はエラーになります。
  const validateError = { exceedStockError: true };
  // ↓ 「購入数」のエラーにしたいので購入数の```control```にエラーをセットします。
  control.get(PRODUCT_PURCHASE_QUANTITY).setErrors(validateError);
  // ↓ 戻り値もエラーを返します。
  return validateError;
};

Unitテスト

Unitテストも念のため掲載します。
こちらも全文載せました。
無駄に長ければすみません。

import { TestBed } from '@angular/core/testing';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';

import {
    PurchaseQuantityStockQuantityValidator
} from './purchase-quantity-stock-quantity-validator';

const PRODUCT_PURCHASE_QUANTITY = 'productPurchaseQuantity';
describe('PurchaseQuantityStockQuantityValidator', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [PurchaseQuantityStockQuantityValidator]
    });
  });

  describe('#validate', () => {
    // ↓ 「在庫数」がブランクの時にエラーがないことを確認
    it('should not have error | productStockQuantity is blank', () => {
      const formBuilder: FormBuilder = new FormBuilder();
      const testingForm: FormGroup = formBuilder.group({
        productStockQuantity: new FormControl(''),
        productPurchaseQuantity: new FormControl(1),
        validatorLocale: new FormControl('ja-JP')
      });
      PurchaseQuantityStockQuantityValidator(testingForm);
      expect(testingForm.get(PRODUCT_PURCHASE_QUANTITY).getError('exceedStockError')).toBeNull();
    });
    // ↓ 「在庫数」が null の時にエラーがないことを確認(上と同じ内容ですが念のため null の時をテストしておく)
    it('should not have error | productStockQuantity is null', () => {
      const formBuilder: FormBuilder = new FormBuilder();
      const testingForm: FormGroup = formBuilder.group({
        productStockQuantity: new FormControl(null),
        productPurchaseQuantity: new FormControl(1),
        validatorLocale: new FormControl('ja-JP')
      });
      PurchaseQuantityStockQuantityValidator(testingForm);
      expect(testingForm.get(PRODUCT_PURCHASE_QUANTITY).getError('exceedStockError')).toBeNull();
    });

    // ↓ 「購入数」がブランクの時にエラーがないことを確認
    it('should not have error | productPurchaseQuantity is blank', () => {
      const formBuilder: FormBuilder = new FormBuilder();
      const testingForm: FormGroup = formBuilder.group({
        productStockQuantity: new FormControl(1),
        productPurchaseQuantity: new FormControl(''),
        validatorLocale: new FormControl('ja-JP')
      });
      PurchaseQuantityStockQuantityValidator(testingForm);
      expect(testingForm.get(PRODUCT_PURCHASE_QUANTITY).getError('exceedStockError')).toBeNull();
    });

    // ↓ 「購入数」が null の時にエラーがないことを確認
    it('should not have error | productPurchaseQuantity is null', () => {
      const formBuilder: FormBuilder = new FormBuilder();
      const testingForm: FormGroup = formBuilder.group({
        productStockQuantity: new FormControl(1),
        productPurchaseQuantity: new FormControl(null),
        validatorLocale: new FormControl('ja-JP')
      });
      PurchaseQuantityStockQuantityValidator(testingForm);
      expect(testingForm.get(PRODUCT_PURCHASE_QUANTITY).getError('exceedStockError')).toBeNull();
    });

    // ↓ 「購入数」と「在庫数」が等しい時にエラーがないことを確認
    it('should not have error | productPurchaseQuantity equals productStockQuantity', () => {
      const formBuilder: FormBuilder = new FormBuilder();
      const testingForm: FormGroup = formBuilder.group({
        productStockQuantity: new FormControl(1),
        productPurchaseQuantity: new FormControl(1),
        validatorLocale: new FormControl('ja-JP')
      });
      PurchaseQuantityStockQuantityValidator(testingForm);
      expect(testingForm.get(PRODUCT_PURCHASE_QUANTITY).getError('exceedStockError')).toBeNull();
    });

    // ↓ 「購入数」が「在庫数」を超えた時はエラーになることを確認
    it('should have error | productPurchaseQuantity exceeds productStockQuantity', () => {
      const formBuilder: FormBuilder = new FormBuilder();
      const testingForm: FormGroup = formBuilder.group({
        productStockQuantity: new FormControl(1),
        productPurchaseQuantity: new FormControl(2),
        validatorLocale: new FormControl('ja-JP')
      });
      PurchaseQuantityStockQuantityValidator(testingForm);
      expect(testingForm.get(PRODUCT_PURCHASE_QUANTITY).getError('exceedStockError')).toBeTruthy();
    });
  });
});

動かしてみる

今日やりたかったのは下記のような感じです。
f:id:tetsufuru:20200304013527p:plain

今日の感想


今日はカスタムバリデーションについて書いてみました。
コードもさほど多くならないし、テストもあまり難しくならなかったので、練習でさらに一つ二つ書いてみるのもいいかもと思いました🌸

では、今日もお疲れ様でした。

Angular 9 で Web アプリを作ろう - わたしも今日からバージョン 9

今日すること

こんにちは、ふるてつです。 年明けついにAngularがバージョン 9 になりました。
今日は製造中の自分のAngularをバージョンアップしてみました。

バージョンアップ情報の確認

まずはこちらのサイトで手順やその他情報をチェックします。
https://update.angular.io/#8.0:9.0

ここに書いてある手順は実際には行いません。
ng updateコマンドで大体のことができるからです。
困ったときなど用にすこし参考にするだけです。

f:id:tetsufuru:20200215171850p:plain f:id:tetsufuru:20200215172728p:plain

TypeScriptは今回3.7にあがりそうです。
materialもあがりますねー。
@angular/localizeのことが書いてあるようですが、わたしは他言語化ngx-translateを使っているので関係なさそうです。
この感じだと今回もng updateのコマンドの実行だけで済みそうです。

ng updateコマンド

早速ですがAngularcling updateコマンドを実行します。
すると下記のリストがでてきました。
今回はclicorematerialの3つのバージョンアップ作業が必要そうです。

ng update
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 39 dependencies.
    We analyzed your package.json, there are some packages to update:
    
      Name                              Version                  Command to update
     -------------------------------------------------------------------------------
      @angular/cli                      8.3.22 -> 9.0.2          ng update @angular/cli
      @angular/core                     8.2.14 -> 9.0.1          ng update @angular/core
      @angular/material                 8.2.3 -> 9.0.0           ng update @angular/material

実際にバージョンアップするコマンドですが、上のリストの最後右端に書いてあります。
コマンド実行する順番はcoreが最初で、次cli、そしてmaterialがこれまでの経験的に良い気がします。
しかし今回はcliを忘れて飛ばしてしまい、cliが最後になりました。

バージョンアップ実施

angular/coreのバージョンアップ

まず最初にcoreをバージョンアップします。
ng update @angular/core

ng update @angular/core    
The installed Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 39 dependencies.
Fetching dependency metadata from registry...
                  Package "@angular-devkit/build-angular" has an incompatible peer dependency to "typescript" (requires ">=3.1 < 3.6", would install "3.7.5").
Incompatible peer dependencies found.
Peer dependency warnings when installing dependencies means that those dependencies might not work correctly together.        
You can use the '--force' option to ignore incompatible peer dependencies and instead address these warnings later.

おや、警告が出ました。
Typescriptがらみのわたしがちょっと苦手なタイプの警告ですね。
Typescriptのバージョンが3.7.5に上がりますが、>=3.1 < 3.6が必要なので上がりすぎます」という旨と思います。
最初にみたサイトでそもそもTypescriptは3.7に上がるようでしたので、ここは無視してバージョンアップを続けます。
(もし動かなければ、Typescriptだけ後でバージョンを下げます)

ではng update @angular/core --forceコマンドを流します(--forceオプションをつけて強制実行)

ng update @angular/core --force
The installed Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 39 dependencies.
Fetching dependency metadata from registry...
                  Package "@angular-devkit/build-angular" has an incompatible peer dependency to "typescript" (requires ">=3.1 < 3.6", would install "3.7.5").
    Updating package.json with dependency @angular/core @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/language-service @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/forms @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/platform-browser @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/animations @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/platform-browser-dynamic @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/common @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/router @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/compiler @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency @angular/compiler-cli @ "9.0.1" (was "8.2.14")...
    Updating package.json with dependency zone.js @ "0.10.2" (was "0.9.1")...
    Updating package.json with dependency typescript @ "3.7.5" (was "3.5.3")...
UPDATE package.json (1658 bytes)
√ Packages installed successfully.
** Executing migrations of package '@angular/core' **

> Static flag migration.
  Removes the `static` flag from dynamic queries.
  As of Angular 9, the "static" flag defaults to false and is no longer required for your view and content queries.
  Read more about this here: https://v9.angular.io/guide/migration-dynamic-flag
  Migration completed.

> Missing @Injectable and incomplete provider definition migration.
  In Angular 9, enforcement of @Injectable decorators for DI is a bit stricter and incomplete provider definitions behave differently.
  Read more about this here: https://v9.angular.io/guide/migration-injectable
  Migration completed.

> ModuleWithProviders migration.
  In Angular 9, the ModuleWithProviders type without a generic has been deprecated.
  This migration adds the generic where it is missing.
  Read more about this here: https://v9.angular.io/guide/migration-module-with-providers
  Migration completed.

> Renderer to Renderer2 migration.
  As of Angular 9, the Renderer class is no longer available.
  Renderer2 should be used instead.
  Read more about this here: https://v9.angular.io/guide/migration-renderer
  Migration completed.

> Undecorated classes with decorated fields migration.
  As of Angular 9, it is no longer supported to have Angular field decorators on a class that does not have an Angular decorator.
  Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes
  Migration completed.

> Undecorated classes with DI migration.
  As of Angular 9, it is no longer supported to use Angular DI on a class that does not have an Angular decorator.
  Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes
  Migration completed.


Your project has been updated to Angular version 9!
For more info, please see: https://v9.angular.io/guide/updating-to-version-9

おっ、良い感じのようです。

angular/materialのバージョンアップ

次はmaterialを上げます。 (順番的にはcliだと思うのですが、忘れてて飛ばしました)
ng update @angular/material

ng update @angular/material
The installed Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 39 dependencies.
Fetching dependency metadata from registry...
    Updating package.json with dependency @angular/material @ "9.0.0" (was "8.2.3")...
    Updating package.json with dependency @angular/cdk @ "9.0.0" (was "8.2.3")...
UPDATE package.json (1658 bytes)
√ Packages installed successfully.
** Executing migrations of package '@angular/material' **

> Updates Angular Material to v9

    ⚠  General notice: The HammerJS v9 migration for Angular Components is not able to migrate tests. Please manually clean up tests in your project if they rely on HammerJS.
    Read more about migrating tests: https://git.io/ng-material-v9-hammer-migrate-tests

      ✓  Updated Angular Material to version 9

UPDATE src/main.ts (373 bytes)
UPDATE package.json (1632 bytes)
/ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site\ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site/ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site\ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site\ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site\ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site/ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site\ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site/ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site/ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site\ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site/ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site\ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site/ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site\ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site| Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site/ Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[ .................] | preinstall:product-manage-site-for-hands-on: info lifecycle product-manage-site- Installing packages...[       ...........] | extract:fsevents: verb lock using C:\Users\tetsuji\AppData\Roaming\npm-cache\_l\ Installing packages...[       ...........] | extract:fsevents: verb lock using C:\Users\tetsuji\AppData\Roaming\npm-cache\_l| Installing packages...[       ...........] | extract:fsevents: verb lock using C:\Users\tetsuji\AppData\Roaming\npm-cache\_l/ Installing packages...[       ...........] | extract:fsevents: verb lock using C:\Users\tetsuji\AppData\Roaming\npm-cache\_l- Installing packages...[        ..........] / extract:bindings: sill extract bindings@1.5.0 extracted to C:\papa\00.default\1\ Installing packages...[         .........] \ extract:file-uri-to-path: sill extract file-uri-to-path@1.0.0 extracted to C:\p| Installing packages...[         .........] \ extract:file-uri-to-path: sill extract file-uri-to-path@1.0.0 extracted to C:\p/ Installing packages...[          ........] | extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] | extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] | extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] | extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse| Installing packages...[          ........] \ extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse/ Installing packages...[          ........] | extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse- Installing packages...[          ........] / extract:nan: sill extract nan@2.14.0 extracted to C:\papa\00.default\12.Benesse\ Installing packages...[           .......] - extract:fsevents: sill extract fsevents@1.2.11 extracted to C:\papa\00.default\| Installing packages...[           .......] - extract:fsevents: sill extract fsevents@1.2.11 extracted to C:\papa\00.default\/ Installing packages...[           .......] - extract:fsevents: sill extract fsevents@1.2.11 extracted to C:\papa\00.default\| Installing packages...[            ......] / finalize:code-point-at: sill finalize C:\papa\00.default\12.Benesse\product-man/ Installing packages...[            ......] / finalize:isarray: sill finalize C:\papa\00.default\12.Benesse\product-manage-si- Installing packages...[            ......] \ finalize:is-fullwidth-code-point: sill finalize C:\papa\00.default\12.Benesse\p\ Installing packages...[            ......] \ finalize:osenv: sill finalize C:\papa\00.default\12.Benesse\product-manage-site| Installing packages...[            ......] | finalize:safe-buffer: sill finalize C:\papa\00.default\12.Benesse\product-manag/ Installing packages...[            ......] \ finalize:strip-json-comments: sill finalize C:\papa\00.default\12.Benesse\produ- Installing packages...[            ......] \ finalize:are-we-there-yet: sill finalize C:\papa\00.default\12.Benesse\product-\ Installing packages...[            ......] / finalize:gauge: sill finalize C:\papa\00.default\12.Benesse\product-manage-site| Installing packages...[            ......] - finalize:glob: sill finalize C:\papa\00.default\12.Benesse\product-manage-site-/ Installing packages...[            ......] - finalize:fs-minipass: sill finalize C:\papa\00.default\12.Benesse\product-manag- Installing packages...[            ......] - finalize:nan: sill finalize C:\papa\00.default\12.Benesse\product-manage-site-f\ Installing packages...[            ......] - finalize:deep-extend: sill finalize C:\papa\00.default\12.Benesse\product-manag| Installing packages...[            ......] - finalize:minimatch: sill finalize C:\papa\00.default\12.Benesse\product-manage-/ Installing packages...[            ......] / finalize:object-assign: sill finalize C:\papa\00.default\12.Benesse\product-man- Installing packages...[            ......] \ finalize:process-nextick-args: sill finalize C:\papa\00.default\12.Benesse\prod\ Installing packages...[            ......] | finalize:sax: sill finalize C:\papa\00.default\12.Benesse\product-manage-site-f| Installing packages...[            ......] - finalize:semver: sill finalize C:\papa\00.default\12.Benesse\product-manage-sit/ Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana- Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana\ Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana| Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana/ Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana- Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana\ Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana| Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana/ Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana- Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana\ Installing packages...[            ......] \ finalize:set-blocking: sill finalize C:\papa\00.default\12.Benesse\product-mana| Installing packages...[            ......] | finalize:signal-exit: sill finalize C:\papa\00.default\12.Benesse\product-manag\ Installing packages...[            ......] - finalize:strip-ansi: sill finalize C:\papa\00.default\12.Benesse\product-manage/ Installing packages...[            ......] | finalize:strip-json-comments: sill finalize C:\papa\00.default\12.Benesse\produ- Installing packages...[            ......] | finalize:strip-json-comments: sill finalize C:\papa\00.default\12.Benesse\produ\ Installing packages...[            ......] | finalize:strip-json-comments: sill finalize C:\papa\00.default\12.Benesse\produ| Installing packages...[            ......] - finalize:gauge: sill finalize C:\papa\00.default\12.Benesse\product-manage-site/ Installing packages...[            ......] - finalize:inflight: sill finalize C:\papa\00.default\12.Benesse\product-manage-s- Installing packages...[            ......] \ finalize:fs-minipass: sill finalize C:\papa\00.default\12.Benesse\product-manag\ Installing packages...[            ......] - finalize:node-pre-gyp: sill finalize C:\papa\00.default\12.Benesse\product-mana| Installing packages...[            ......] \ finalize:fsevents: sill finalize C:\papa\00.default\12.Benesse\product-manage-s/ Installing packages...[            ......] / finalize:code-point-at: sill finalize C:\papa\00.default\12.Benesse\product-man- Installing packages...[            ......] - finalize:deep-extend: sill finalize C:\papa\00.default\12.Benesse\product-manag\ Installing packages...[            ......] - finalize:minimatch: sill finalize C:\papa\00.default\12.Benesse\product-manage-| Installing packages...[            ......] / finalize:npm-bundled: sill finalize C:\papa\00.default\12.Benesse\product-manag/ Installing packages...[            ......] / finalize:object-assign: sill finalize C:\papa\00.default\12.Benesse\product-man- Installing packages...[            ......] / finalize:nopt: sill finalize C:\papa\00.default\12.Benesse\product-manage-site-\ Installing packages...[            ......] - finalize:safer-buffer: sill finalize C:\papa\00.default\12.Benesse\product-mana| Installing packages...[            ......] - finalize:semver: sill finalize C:\papa\00.default\12.Benesse\product-manage-sit/ Installing packages...[            ......] | finalize:strip-json-comments: sill finalize C:\papa\00.default\12.Benesse\produ- Installing packages...[            ......] - finalize:util-deprecate: sill finalize C:\papa\00.default\12.Benesse\product-ma\ Installing packages...[            ......] / finalize:once: sill finalize C:\papa\00.default\12.Benesse\product-manage-site-| Installing packages...[            ......] | finalize:rimraf: sill finalize C:\papa\00.default\12.Benesse\product-manage-sit/ Installing packages...[            ......] / finalize:tar: sill finalize C:\papa\00.default\12.Benesse\product-manage-site-f- Installing packages...[            ......] - finalize:node-pre-gyp: sill finalize C:\papa\00.default\12.Benesse\product-mana- Installing packages...[            ......] / refresh-package-json:deep-extend: sill refresh-package-json C:\papa\00.default\\ Installing packages...[            ......] - refresh-package-json:safe-buffer: sill refresh-package-json C:\papa\00.default\| Installing packages...[            ......] - refresh-package-json:safe-buffer: sill refresh-package-json C:\papa\00.default\/ Installing packages...[            ......] - refresh-package-json:safe-buffer: sill refresh-package-json C:\papa\00.default\- Installing packages...[            ......] - refresh-package-json:safe-buffer: sill refresh-package-json C:\papa\00.default\\ Installing packages...[            ......] - refresh-package-json:safe-buffer: sill refresh-package-json C:\papa\00.default\/ Installing packages...[             .....] | postinstall:product-manage-site-for-hands-on: info lifecycle product-manage-sit\ Installing packages...[              ....] - prepare:product-manage-site-for-hands-on: info lifecycle product-manage-site-fo\ Installing packages...[              ....] / prepare:product-manage-site-for-hands-on: info lifecycle product-manage-site-fonpm WARN @angular-devkit/build-angular@0.803.22 requires a peer of @angular/compiler-cli@^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @angular-devkit/build-angular@0.803.22 requires a peer of typescript@>=3.1 < 3.6 but none is installed. You must install peer dependencies yourself.
npm WARN @ngtools/webpack@8.3.22 requires a peer of @angular/compiler-cli@^8.0.0 but none is installed. You must install peer 
dependencies yourself.
npm WARN @ngtools/webpack@8.3.22 requires a peer of typescript@>=3.4 < 3.6 but none is installed. You must install peer dependencies yourself.
npm WARN karma-jasmine-html-reporter@1.5.1 requires a peer of jasmine-core@>=3.5 but none is installed. You must install peer 
dependencies yourself.
npm WARN ngx-translate-testing@3.0.0 requires a peer of @angular/common@^8.0.0-rc.0 || ^8.0.0 but none is installed. You must 
install peer dependencies yourself.
npm WARN ngx-translate-testing@3.0.0 requires a peer of @angular/core@^8.0.0-rc.0 || ^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.2 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
/ Installing packages...WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.11 (node_modules\webpack-dev-server\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.11: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.11 (node_modules\watchpack\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.11: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\karma\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

removed 1 package and audited 16850 packages in 54.761s
found 2 high severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details
√ Packages installed successfully.
  Migration completed.

** Executing migrations of package '@angular/cdk' **

> Updates the Angular CDK to v9

      ✓  Updated Angular CDK to version 9

  Migration completed.

こちらも良い感じです。
脆弱性の警告が2件ほど出たもようなので、作業がひとしきり終わったら対応しようと思います(npm audit fix

angular/cliのバージョンアップ

3番目にng update @angular/cli

ng update @angular/cli
The installed Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 38 dependencies.
Fetching dependency metadata from registry...
    Updating package.json with dependency @angular/cli @ "9.0.2" (was "8.3.22")...
UPDATE package.json (1630 bytes)
√ Packages installed successfully.
** Executing migrations of package '@angular/cli' **

> Angular Workspace migration.
  Update an Angular CLI workspace to version 9.
UPDATE angular.json (3915 bytes)
UPDATE tsconfig.app.json (272 bytes)
UPDATE package.json (1633 bytes)
√ Packages installed successfully.
  Migration completed.

> Lazy loading syntax migration.
  Update lazy loading syntax to use dynamic imports.
  Migration completed.

> Replace deprecated 'styleext' and 'spec' Angular schematic options.
  Migration completed.

こちらも問題なく上がりました。

脆弱性の修正

あとは先ほどの脆弱性をこちらのコマンドで修正します。
npm audit fix

npm audit fix

> core-js@3.6.0 postinstall C:\papa\00.default\12.Benesse\product-manage-site-for-hands-on\node_modules\core-js
> node -e "try{require('./postinstall')}catch(e){}"

Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!

The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: 
> https://opencollective.com/core-js 
> https://www.patreon.com/zloirock 

Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)

npm WARN karma-jasmine-html-reporter@1.5.1 requires a peer of jasmine-core@>=3.5 but none is installed. You must install peer 
dependencies yourself.
npm WARN ngx-translate-testing@3.0.0 requires a peer of @angular/common@^8.0.0-rc.0 || ^8.0.0 but none is installed. You must 
install peer dependencies yourself.
npm WARN ngx-translate-testing@3.0.0 requires a peer of @angular/core@^8.0.0-rc.0 || ^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN sass-loader@8.0.0 requires a peer of node-sass@^4.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN sass-loader@8.0.0 requires a peer of fibers@>= 3.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN webpack-subresource-integrity@1.3.4 requires a peer of html-webpack-plugin@^2.21.0 || ~3 || >=4.0.0-alpha.2 <5 but none is installed. You must install peer dependencies yourself.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.11 (node_modules\webpack-dev-server\node_modules\fsevents):      
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.11: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.11 (node_modules\watchpack\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.11: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\karma\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.2 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

+ @angular-devkit/build-angular@0.900.2
added 196 packages from 108 contributors, removed 31 packages, updated 223 packages and moved 2 packages in 310.024s
fixed 2 of 2 vulnerabilities in 16850 scanned packages

2件の脆弱性は良い感じに修正できた模様です。

バージョン確認

ここでAngularのバージョンを確認してみます。
ng version

ng version
? Would you like to share anonymous usage data about this project with the Angular Team at
Google under Google’s Privacy Policy at https://policies.google.com/privacy? For more     
details and how to change this setting, see http://angular.io/analytics. No

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 9.0.2
Node: 12.4.0
OS: win32 x64

Angular: 9.0.1
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.900.2
@angular-devkit/build-angular     0.900.2
@angular-devkit/build-optimizer   0.900.2
@angular-devkit/build-webpack     0.900.2
@angular-devkit/core              9.0.2
@angular-devkit/schematics        9.0.2
@angular/cdk                      9.0.0
@angular/cli                      9.0.2
@angular/material                 9.0.0
@ngtools/webpack                  9.0.2
@schematics/angular               9.0.2
@schematics/update                0.900.2
rxjs                              6.5.4
typescript                        3.7.5
webpack                           4.41.2

良いですねぇ、ちゃんと 9 に上がってます。
レンダリングもIvyに変わったようです(Ivy Workspace: Yes)。

動作確認

起動の確認

では動くかどうか起動してみます。
ng serve --o s(起動コマンドです、少し省略してます)

ng serve --o s
0% compiling
Compiling @angular/core : es2015 as esm2015

Compiling @angular/common : es2015 as esm2015

Compiling @angular/platform-browser : es2015 as esm2015

Compiling @angular/platform-browser-dynamic : es2015 as esm2015

Compiling @angular/animations : es2015 as esm2015

Compiling @angular/animations/browser : es2015 as esm2015

Compiling @angular/platform-browser/animations : es2015 as esm2015

Compiling @angular/router : es2015 as esm2015

Compiling @angular/common/http : es2015 as esm2015

Compiling @angular/forms : es2015 as esm2015

Compiling @angular/cdk/keycodes : es2015 as esm2015

Compiling @angular/cdk/platform : es2015 as esm2015

Compiling @angular/cdk/observers : es2015 as esm2015

Compiling @angular/cdk/a11y : es2015 as esm2015

Compiling @angular/cdk/bidi : es2015 as esm2015

Compiling @angular/material/core : es2015 as esm2015

Compiling @angular/material/button : es2015 as esm2015

Compiling @angular/cdk/collections : es2015 as esm2015

Compiling @angular/cdk/scrolling : es2015 as esm2015

Compiling @angular/cdk/portal : es2015 as esm2015

上記のようになにかのCompilingがいっぱい走りましたが、起動は問題なくできました。

Unitテストの確認

Unitテストは最初動かなかったです、下のような感じになりました。

f:id:tetsufuru:20200215200334p:plain f:id:tetsufuru:20200215200351p:plain

このエラーは「hammerjs」によるもので「test.ts」でインポートしていたhammerjsを消すと治まりました。
下記の差分のような感じです。
f:id:tetsufuru:20200215212052p:plain

これまでAngularのUnitテストはhammerjsに依存していましたが、今回のバージョンでそれをなくしたそうです。
そのため手動で「test.ts」からhammerjsのインポート文を消す必要があるようです。
https://github.com/angular/components/blob/master/guides/v9-hammerjs-migration.md

E2Eテストの確認

E2Eテストは問題なく動きました。
これで今日のミッション完了ですな✨

今日の感想


年末から年始にかけてずっとAngularSpring Bootをさわっていたのですが、ブログに書く機会がなく、久しぶりの投稿になりました。
他に書きたいことがだいぶ溜まっているので忘れないうちに書かねばと思いました😎

余談になりますが、Unitテストの時に使う、#TestBed.get()メソッドがDeprecatedになっていました、ブログを書き終えた後に気づきました。
今後は#TestBed.inject()を使いましょうー。

では、本年もよろしくお願いします。

今夜は社内AWSもくもく会2 - Route53でDNSを設定する😊

今日すること

こんにちはふるてつです。

前回勉強したS3に引き続き、今回はRout53を利用してDNSを設定します。
今日はそのお話になります。

ドメインを取得する

本によると作業を進めるためには、まず独自のドメインを取得しないとなりません。
安い所を探そうと思っていましたが、とある勉強会で独自ドメインのクーポン券をいただいたのを思い出しました。
GMOぺパポさんの「MuuMuuDomain」という名前のサービスです。
https://muumuu-domain.com/
下はそのクーポン券です。
今日はこれを使います、1年間だけ無料で登録できます。

f:id:tetsufuru:20191120191841p:plain:w400

f:id:tetsufuru:20191120191949p:plain:w400

ムームーさんのサイトに行ってみます🍉 f:id:tetsufuru:20191120192501p:plain

取得したいドメインを検索します。
取得可能であれば、値段が表示され出てきますので、カートに追加します。
f:id:tetsufuru:20191120193750p:plain

カートに追加したあとそのまま購入します。
f:id:tetsufuru:20191120194037p:plain

f:id:tetsufuru:20191120194546p:plain クーポンを使用するので無料ですが、クレジットカード番号は入れねばなりません。
オプションなどは選択しません。
途中でクーポンコードを登録する箇所があるので忘れずに入力します。

f:id:tetsufuru:20191120195055p:plain

f:id:tetsufuru:20191120195152p:plain 料金を確認して「取得する」ボタンをクリックします。

f:id:tetsufuru:20191120195431p:plain いったんこれで完了🥝

Rout 53の設定

さあそれではAWSの作業に入ります。

f:id:tetsufuru:20191120195605p:plain Rout53をメニューから選択します。

f:id:tetsufuru:20191120195713p:plain なんだかすごそうな画面が出てきました、新しい画面を開くときはいつもすこしワクワクしますねー
では「DNS管理」を選びます。

f:id:tetsufuru:20191120195951p:plain 上記画面で「ホストゾーンの作成」をクリックします。

f:id:tetsufuru:20191120200224p:plain もう1回、「ホストゾーンの作成」をクリックします。

f:id:tetsufuru:20191120200516p:plain 画面右側にホストゾーンの作成欄が表示されますので、「ドメイン名」、「コメント」を入力します。
わたしは外部で独自ドメインを取得していますので「タイプ」はパブリックホストゾーンを選択します。 準備ができたら「作成」ボタンをクリックします。

f:id:tetsufuru:20191120201042p:plain 上記のように、作成できました。
次に「レコードセットの作成」をクリックします。

f:id:tetsufuru:20191120201710p:plain 「レコードセットの作成」をクリックすると上記の画面右側に、レコードセットの作成画面が表示されます。
そこで「名前」欄は"www'にします。「タイプ」は"CNAME"にします(書籍の通り)
「値」欄には前回作成したS3バケットのエンドポイントを入力します。 エンドポイント先頭の "http://" は画面上に書いてあるコメントに合わせて消しました(書籍では消していません)

f:id:tetsufuru:20191120202508p:plain これで作成完了🍊

独自ドメイン側での設定

上記の作業で終わりかと思っていたところ正しく動きませんでした🤢
もくもく仲間の同僚に聞いたところ、独自ドメイン側で設定が必要だそうです。
良く考えるとそれはそうですね。ムームー側がなにも知らないでつながるわけがありません。
そこで下記のサイトを参考に追加で設定しました。
http://webfood.info/muumuu-domain-for-amazon-s3/

もう一度ムームードメインに戻ります。 コントロールパネルを開きます。 f:id:tetsufuru:20191120203312p:plain コントロールパネルの下の方に「ネームサーバ」1~4の欄があります。
そこにRout53側のネームサーバを登録します。
Rout53側のネームサーバは下記画面でレコードセットのタイプ "NS"をクリックすると、画面右側の「値」欄に表示されます。
それらをムームードメインに登録する感じになります。
f:id:tetsufuru:20191120204815p:plain

f:id:tetsufuru:20191120205454p:plain 設定変更が完了しました。
ではS3のエンドポイントではなく、独自のドメインの方から呼び出してみます。
f:id:tetsufuru:20191120205639p:plain S3に置いてある index.html が表示されました。
「ようこそ!!」しか画面には出ませんが、これで繋がるようになりました。

感想


今回は自分独自のドメインを作ってのRout53でした。
こういう勉強をしていなければドメインを作るようなことはなかったと思います。
おじさんになっても知らないことは多いですねぇ。 まあ、あせらずその都度勉強していくしかないかと思う今週でした。

それではまた😎

今度は客先でコンテナ勉強会

今日すること

f:id:tetsufuru:20190806104529p:plain:w100
こんにちはふるてつです。

ただいま客先の勉強会にてDockerを勉強中で、まずは入門としてこちらのサイト「入門Docker」を勉強しております。
https://y-ohgi.com/introduction-docker/
今回は少なめですが、「プロダクションでの活用」 メニューの中から「デバッグ」ページをもくもくとしました。
「設計」と「セキュリティ」のところは読むだけで終わりましたので 今日は「デバッグ」です。 またいつものように勉強した内容を書きます。

1. コンテナのライフサイクル

f:id:tetsufuru:20191010180504p:plain ライフサイクルを意識することは重要だそうです。
コンテナの状態の確認方法をまずは勉強していきます。

1-1. 短命なコンテナ

docker runpythonで"hoge"と出力するコンテナを起動してみます。
"hoge"と表示しただけで終わる、これはたしかに短命ですな。

docker run python python -c "print('hoge')"
hoge

現在のホスト上の全てのコンテナを見ます。

docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
fc4be9b622af        python              "python -c print('ho…"   11 seconds ago      Exited (0) 9 seconds ago                       elastic_hypatia

STATUSが "Exited (0) 9 seconds ago" となっています、もう終わってますね。
もう1回print('hoge')を実行してみます、そしてコンテナを確認します。

docker run python python -c "print('hoge')"
hoge
PS C:\Users\ts-tetsuji.furukawa> docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
20fa58fd83b0        python              "python -c print('ho…"   5 seconds ago       Exited (0) 3 seconds ago                       interesting_heyrovsky
fc4be9b622af        python              "python -c print('ho…"   5 minutes ago       Exited (0) 5 minutes ago                       elastic_hypatia

今度は2つになりました。
実行される度に新しい環境 が立ち上がるようです。
以前立ち上げたコンテナを動かしたいときはdocker start -a [CONTAINER ID]コマンドを実行すると良いそうです、なるほど。

docker start -a 20fa58fd83b0
hoge

1-2. 長命なコンテナ

Webサーバのように起動し続けるタイプのコンテナを見てみます。
docker run -P -d nginx

コンテナの状態を確認してみます。

docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                   NAMES
1a8b6df0862c        nginx               "nginx -g 'daemon of…"   53 seconds ago      Up 52 seconds               0.0.0.0:32768->80/tcp   cocky_cerf
20fa58fd83b0        python              "python -c print('ho…"   20 minutes ago      Exited (0) 10 minutes ago                           interesting_heyrovsky
fc4be9b622af        python              "python -c print('ho…"   26 minutes ago      Exited (0) 26 minutes ago                           elastic_hypatia

STATUSが "Up 52 seconds" となっています、これはまだ動いてますね。
nginxをもう一つ起動します。
nginxが下記のように一つ増えました。

docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                   NAMES
31035305c436        nginx               "nginx -g 'daemon of…"   5 seconds ago       Up 4 seconds                0.0.0.0:32769->80/tcp   awesome_murdock
1a8b6df0862c        nginx               "nginx -g 'daemon of…"   16 minutes ago      Up 16 minutes               0.0.0.0:32768->80/tcp   cocky_cerf
20fa58fd83b0        python              "python -c print('ho…"   36 minutes ago      Exited (0) 26 minutes ago                           interesting_heyrovsky
fc4be9b622af        python              "python -c print('ho…"   41 minutes ago      Exited (0) 41 minutes ago                           elastic_hypatia

では一旦これまでに起動したコンテナを削除します。
まずコンテナを停止します。

docker stop $(docker container ls -q)

次にコンテナを削除します。

docker rm $(docker container ls -aq)

そしてイメージを削除します。

docker rmi $(docker images -q)

と参考にしたサイトには書いてあります。
しかし全部消すのであれば、わたしとしては下のコマンドで一掃したいですね。
これは使われていないコンテナやイメージを一気に消してくれます。

docker system prune

あとは残ったコンテナやイメージを最初のコマンドで停止・削除します。

2. コンテナの中に入る

起動したコンテナの中に入ることも可能です。
これまでの作業ですべてのコンテナを削除してしまいましたので、もう一度ngnxを起動します。
ホスト側のブラウザからnginxにアクセスしたいのでポートを設定しておきます。
docker run -d -p 8080:80 nginx

run -d -p 8080:80 nginx
fa94b6da6e0e63abc33eca78e061c5921f24f7aee3b07b498a16df0a946874af
PS C:\Users\tetsuji> docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
fa94b6da6e0e        nginx               "nginx -g 'daemon of…"   14 minutes ago      Up 14 minutes       0.0.0.0:8080->80/tcp   xenodochial_merkle

2-1. 起動したコンテナの中に入る

下のコマンドを実行すると先ほど起動したコンテナに入ることができます。
コンテナIDを指定してbashで入ります。
docker exec -it {Container ID} bash

例えばnginxファイルの存在を確認してみます。
たしかにnginxのファイルが見えますね。

docker exec -it 0b3901f01b82 bash
root@0b3901f01b82:/# ls /usr/share/nginx/html/
50x.html  index.html

あとコマンドのインストールなどもしてみます。

root@fa94b6da6e0e:/# apt-get update
root@fa94b6da6e0e:/# apt-get install -y curl procps

実際nginxが起動しているか、 localhost:80 へアクセスしてみます。
動いてますねー
f:id:tetsufuru:20191109004034p:plain

下記のようにnginxのindex.htmlを変えることもできます。

root@fa94b6da6e0e:/# echo "<h1>hello docker</h1>" > /usr/share/nginx/html/index.html
root@fa94b6da6e0e:/# cat /usr/share/nginx/html/index.html
<h1>hello docker</h1>

書き変わりました。
f:id:tetsufuru:20191109005247p:plain

docker logs {Container ID}コマンドでログを見ることも可能です。
起動したプロセスの 標準出力と標準エラーを見ることができるそうです。

docker logs fa94b6da6e0e
172.17.0.1 - - [08/Nov/2019:15:21:57 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" "-"
2019/11/08 15:21:58 [error] 7#7: *2 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: 
"localhost:8080", referrer: "http://localhost:8080/"
172.17.0.1 - - [08/Nov/2019:15:21:58 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://localhost:8080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" "-"
172.17.0.1 - - [08/Nov/2019:15:52:18 +0000] "GET / HTTP/1.1" 200 22 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" "-"

2-2. 停止したコンテナの中に入る

最後は停止したコンテナから新たにイメージを作る方法です。
amazonlinux2を起動して/tmp/中にhogeフォルダを作ります。
コマンドが終わればコンテナは停止します。
docker run amazonlinux:2 mkdir /tmp/hoge

docker run amazonlinux:2 mkdir /tmp/hoge
Unable to find image 'amazonlinux:2' locally
2: Pulling from library/amazonlinux
17282fad1a5e: Pull complete
Digest: sha256:5aa0460abffafc6a76590f0070e1b243a93b7bbe7c8035f98c1dee2f9b46f44c
Status: Downloaded newer image for amazonlinux:2
PS C:\Users\tetsuji> docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
8f67ae93d41c        amazonlinux:2       "mkdir /tmp/hoge"   18 seconds ago      Exited (0) 17 seconds ago                       youthful_edison

docker commitコマンドで停止したコンテナからexited-containerという名前の新たなイメージを作ります。

docker commit 8f67ae93d41c exited-container
sha256:de46c9f656fcaab46266762d07524ab42a4b68e6e70a928ac2008bd9277fff32

新たなイメージを起動して、コンテナ内で作成したディレクトリが存在するかどうか確認します。

docker run -it exited-container bash
bash-4.2# ls /tmp/
hoge

たしかにありました。

今日の感想


今回もまた「入門Docker」の内容をそのまま、まとめた内容になりました。
基本的にはデバッグの仕方を練習してみた感じでした。
Docker初心者なのでどういったデバッグのシチュエーションがあるのか、あまり想像がついていませんがなんとなく一通りのことはできそうかなと思いました。

それではまた✨

今夜は社内AWSもくもく会2 - やっとS3まできたぞ😊

今日すること

こんにちはふるてつです。

前回はElastic Beanstalkについてでしたが、 今回はS3について書きます。

いままでのおさらいになりますが、わたしは客先の仕事が終わった後定期的に、自社に戻ってAWS環境を勉強しています。
基本的には下記の本のとおりにさわっているだけです。
f:id:tetsufuru:20191106182429p:plain:w400

いま半分を少し過ぎたところで、やっとS3まで来ました。
f:id:tetsufuru:20191106182456p:plain:w400

S3について余談ですが、わたしこれまでに仕事で使ったことはありますね。
例えば、JavaでデータをS3にアップロードしたり、S3から読み込んだり、消したり消さなかったり。
しかし実は空の状態から作ったことがなかったので、ちょうどいい勉強になるかなと思っています。

S3バケットの作成

ではまずS3バケットを作っていきます。
サービスの「S3」から「バケット」で下の画面が表示されます。
バケットを作成する」をクリックします。
f:id:tetsufuru:20191106185252p:plain

f:id:tetsufuru:20191106185428p:plain 上記の画面が出てきます。

f:id:tetsufuru:20191106190021p:plain 必要な項目を入力します。
バケット名」は任意の名前で構いません、しかしS3全体の中で一意なので、 他と被らないような名前にします。
「リージョン」はアジアパシフィック(東京)にして「次へ」をクリック。

f:id:tetsufuru:20191106190854p:plain 上記の画面はそのまま「次へ」をクリックします。

f:id:tetsufuru:20191106191050p:plain 上記はアクセス許可の設定、こちらもデフォルトのまま「次へ」
(アクセス許可をここで設定して構いません、しなかったのは本がデフォルトだったからです)

f:id:tetsufuru:20191106191425p:plain 最後に「バケットを作成」をクリック。
バケットが出来ました。

f:id:tetsufuru:20191106191631p:plain

ブロックパブリックアクセスの設定

それでは次に「アクセス権限」タブ ⇒ 「ブロックパブリックアクセス」で、アクセス権限の設定をおこないます。
f:id:tetsufuru:20191106195553p:plain 現在はすべてのアクセスがブロックされていますので、右横の「編集」をクリックして設定を変更します。

f:id:tetsufuru:20191106195701p:plain 上記画面中の下2つ、「パケットポリシー」に関するブロックをoffにして保存します。

バケットポリシーの設定

次に下記画面にて「パケットポリシー」をクリックします。
f:id:tetsufuru:20191106200320p:plain 画面中央のパケットポリシーエディターにてパケットポリシーを設定します。
設定はjsonで行います、内容は下記です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::furutest1234567/*"
        }
    ]
}

jsonのサンプルは下記を参考にしましたが、なかなか書くのがめんどくさいですねぇ。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-bucket-policies.html
"Effect": "Allow"、"Principal": "*"なので内容的にはすべてのユーザーに許可を与えています。
"Version": "2012-10-17"はこのままにしておきます。
最新でいいかと思って今日の日付に変えたらエラーになりました🤢
f:id:tetsufuru:20191106200526p:plain 上記のようにjsonを入力後「保存」します。

Static website hostingの設定

このバケットを静的サイトとして使用するための設定を行います。
「プロパティ」のタブをクリックします。
f:id:tetsufuru:20191106204452p:plain

「Static website hosting」を選択すると下記の画面になります。 画面の上の方に「エンドポイント」が表示されています。
ユーザーがS3にアクセスする際はこのエンドポイントを指定します。
f:id:tetsufuru:20191106230411p:plain そして上記のように「インデックスドキュメント」に"index.html"、
「エラードキュメント」に"error.html"を入力します。
「インデックスドキュメント」はその名の通り、サイトを訪れたユーザーに最初にアクセスさせたいページです。
「エラードキュメント」はエラー時に表示させたいページになります。

コンテンツのアップロード

上記で登録した"index.html"と"error.html"をアプロードします。 f:id:tetsufuru:20191106205349p:plain S3の画面に戻って「アップロード」ボタンをクリックします。

f:id:tetsufuru:20191106205525p:plain 2ファイルを選択してアップロードします。

f:id:tetsufuru:20191106205804p:plain アップロードが完了しました。
ではエンドポイントにアクセスしてみます。
"index.html"の画面が表示されます。
f:id:tetsufuru:20191106233247p:plain 次は存在しないファイルを指定します。 f:id:tetsufuru:20191106233347p:plain 今度はエラー画面が表示されました、なるほど。

リダイレクトルールの設定

次にリダイレクトルールを設定します。
「Static website hosting」画面の下の方のエリアです。
f:id:tetsufuru:20191106231822p:plain リダイレクトの内容は下記になります。

<RoutingRules>
  <RoutingRule>
    <Condition>
      <KeyPrefixEquals>foo/</KeyPrefixEquals>
    </Condition>
    <Redirect>
      <ReplaceKeyPrefixWith>bar/</ReplaceKeyPrefixWith>
    </Redirect>
  </RoutingRule>
</RoutingRules>

上記で"foo/"フォルダへのアクセスは"bar/"にリダイレクトされます。
S3に"bar"フォルダを作成し、そこにリダイレクトのテスト用のhtmlをアップロードします。
f:id:tetsufuru:20191106234352p:plain

試しにxxxx/foo/redirect_test.htmlにアクセスしてみます。
下記のようにxxxx/bar/redirect_test.htmlが表示されました。
f:id:tetsufuru:20191106234818p:plain

RoutingRuleは複数追加できます。
例えば下記ですが、404エラー時にのみ指定したhtmlを表示するルールを追加しました。

<RoutingRules>
  <RoutingRule>
    <Condition>
      <KeyPrefixEquals>foo/</KeyPrefixEquals>
    </Condition>
    <Redirect>
      <ReplaceKeyPrefixWith>bar/</ReplaceKeyPrefixWith>
    </Redirect>
  </RoutingRule>
  <!-- 追加 -->
  <RoutingRule>
    <Condition>
      <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
    </Condition>
    <Redirect>
      <ReplaceKeyWith>404.html</ReplaceKeyWith>
    </Redirect>
  </RoutingRule>
  <!-- 追加 -->
</RoutingRules>

最後にこちらも試してみます。
また存在しないアドレスを指定します。
今度はエラー画面(error.html)ではなく、404用のエラー画面(404.html)が表示されました。
f:id:tetsufuru:20191106235957p:plain

感想


S3を空の状態から作ってみましたが、いざやってみるとわからないことだったり、新たに知ることがあったりでわりと新鮮でした。
リダイレクトの機能がすこし面白かったですねー。
次はS3に加えてRoute53が登場します。
それまでに自分のドメインを取っておいたほうが良いそうですね。どこか安いドメインをさがさないと~。

それではまた😎

いまだにJava 8 備忘録 - MyBatis Genaratorの使い方と Flyway

今日すること

こんにちはふるてつです。
ちょっとした新シリーズになります。
最近業務外でJavaを良く触るので、忘れそうな内容を備忘録として書きとめておきたいと思います。

f:id:tetsufuru:20191019103646p:plain

タイトルの通りおじさんはいまだにJava8です。
基本的にはいつもSpring Bootを使っています。
ちなみに業務でもJavaですが、コードを書く機会が減ったので、ほぼほぼ家に帰ってからさわる毎日です✨
今回は数年ぶりにMyBatisを使ったのでそのことを記事にしました。

MyBatisとは昔からあるO/Rマッパーの事です。
相当昔からあり、一時期はどこの客先に行ってもこれを使っていたように思います。
f:id:tetsufuru:20191019103308p:plain
https://mybatis.org/mybatis-3/ja/index.html

新規プロジェクトの作成

まずはEclipseにて新しいプロジェクトを作成します。
STSプラグインは事前にダウンロードしておきます)

「新規」 ⇒ 「その他」
f:id:tetsufuru:20191019105051p:plain

そして「Springスターター・プロジェクト」を選択します。 f:id:tetsufuru:20191019105137p:plain:w400

わたしはGradleを良く使います。
f:id:tetsufuru:20191019105533p:plain:w400

プロジェクトの依存関係ですが、 FlywayMyBatisH2Spring WebLombokSpring Dev Toolsを選びます。
f:id:tetsufuru:20191019105714p:plain:w400

このまますすめて完了。
f:id:tetsufuru:20191019105947p:plain:w400

下記のようにプロジェクトが出来ました。
f:id:tetsufuru:20191019110031p:plain

H2環境の設定

つぎはH2を使用するための設定をEclipseapplication.propertiesに追加します。 f:id:tetsufuru:20191019110523p:plain

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/sampledb
spring.datasource.username=username
spring.datasource.password=password

DDLの作成

次にsrc/main/resourcesの配下にdb/migrationフォルダを作り、そこに DDL文を配置します。
上記の写真ではV1__create_schema_for_domain.sqlという名前になっています。
この状態でいったんEclipseを起動します。
起動するとFlywayが勝手にDDL文を流してくれ、テーブルがH2の中にできます。
正常に起動したらいったん終了しておきます。
f:id:tetsufuru:20191019111326p:plain

MyBatis Generatorのインストール

テーブルが無事にできたところでMyBatis GenaratorEclipseにインストールします。
マーケットプレイスからインストールします。
(下記の写真はインストール後になります) f:id:tetsufuru:20191019111515p:plain:w400

generator config の設定

generationConfig.xmlファイルをリソースの中に作成します。
リソースであれば場所はどこでも構わないと思います。
f:id:tetsufuru:20191019111646p:plain:w400

f:id:tetsufuru:20191019111843p:plain

細かい設定内容は下記ですが、若干コツがあります。
Xmlファイルの出力パス( )とMapperの出力パス( )とをそろえると、 いい具合にMyBatisがお互いを関連づけて認識してくれます。
ことなる場合、ちょっと忘れましたが、なにがしらの設定を追記しなければなりません。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
  <context id="handsOnTables" targetRuntime="MyBatis3">

    <!-- Connection Settings -->
    <jdbcConnection driverClass="org.h2.Driver"
        connectionURL="jdbc:h2:../workspace/mybatis-demo/h2db/sampledb"
        userId="username"
        password="password">
    </jdbcConnection>

    <!-- Entity Models -->
    <javaModelGenerator targetPackage="com.example.demo.entity.domain" targetProject="mybatis-demo/src/main/java">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <!-- Sql Xml files -->
    <sqlMapGenerator targetPackage="com.example.demo.mybatis" targetProject="mybatis-demo/src/main/resources">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

    <!-- Mappers -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.example.demo.mybatis" targetProject="mybatis-demo/src/main/java">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

    <!-- Target tables -->
    <table schema="" tableName="USER_MST">
      <property name="mapUnderscoreToCamelCase" value="true" />
    </table>
    <table schema="" tableName="PRODUCT_MST">
      <property name="mapUnderscoreToCamelCase" value="true" />
    </table>
    <table schema="" tableName="PRODUCT_STOCK_MST">
      <property name="mapUnderscoreToCamelCase" value="true" />
    </table>
    <table schema="" tableName="PRODUCT_PURCHASE_TBL">
      <property name="mapUnderscoreToCamelCase" value="true" />
    </table>
    <table schema="" tableName="SUB_MENU_MST">
      <property name="mapUnderscoreToCamelCase" value="true" />
    </table>
    <table schema="" tableName="MENU_MST">
      <property name="mapUnderscoreToCamelCase" value="true" />
    </table>

  </context>
</generatorConfiguration>

MyBatis Generatorの実行

Eclipseのメニュー から 「実行」 ⇒ 「実行環境」を選択します。 f:id:tetsufuru:20191019113149p:plain 下記の実行構成画面でMyBatis Generatorを右クリック ⇒ 「新規作成」を選びます。
f:id:tetsufuru:20191019113544p:plain:w400

「名前」を入力し、「構成ファイル」はワークスペースから先ほどのgenerationConfig.xmlファイルを選択します。
f:id:tetsufuru:20191019114417p:plain:w400

Eclipseの画面左下のランナーに「MyBatis Generator」が表示されるようになりました。
これを右クリック ⇒ 「実行」でgeneratorが起動します。
f:id:tetsufuru:20191019114949p:plain

Configで設定した場所にentityが出来ました。 f:id:tetsufuru:20191019115221p:plain

Mapperができました。
f:id:tetsufuru:20191019115303p:plain

Xmlファイルができました。
下記ファイルのなかに自動生成されたSQL文が入っています。
自作のSQLを実行したい場合は、こちらに追記し、上記の Mapperに呼び出し用のメソッドを追加します。
ちなみに自動生成をやり直しても自作部分は消えないようになっています。
f:id:tetsufuru:20191019115341p:plain

初期データの投入

では少し試してみたいので初期データを投入してみます。 V2__import-initial-data.sqlというファイルを追加して、そのなかに初期データのInsert文を追加します。
これもまた次回Eclipseを起動したときにFlywayが流してくれます。
f:id:tetsufuru:20191019120453p:plain

H2からデータを取得

では簡単なコントローラとサービスを作ってMyBatisを試してみます。 まずコントローラを作ります。
f:id:tetsufuru:20191019120823p:plain

次にサービスを作ります。
f:id:tetsufuru:20191019121006p:plain

サービスの細かいコードは下記です。

package com.example.demo.service.rest;        
        
import java.util.List;      
        
import org.springframework.stereotype.Service;      
        
import com.example.demo.entity.domain.UserMst;      
import com.example.demo.entity.domain.UserMstExample;       
import com.example.demo.mybatis.UserMstMapper;      
        
import lombok.RequiredArgsConstructor;      
        
@Service        
@RequiredArgsConstructor        
public class MyBatisTestRestService {       
        
    private final UserMstMapper userMstMapper;  
        
    public void myBatisTest() { 
        
        // 例1)主キーで検索
        UserMst userMst = userMstMapper.selectByPrimaryKey(Long.valueOf(1));
        
        System.out.println("UserSeq:" + userMst.getUserSeq());
        System.out.println("UserName:" + userMst.getUserName());
        
        //  例2)条件を指定して検索
        UserMstExample userMstExample = new UserMstExample();
        userMstExample.createCriteria().andUserAccountEqualTo("user01");
        List userMsts = userMstMapper.selectByExample(userMstExample);
        
        System.out.println("UserSeq:" + userMsts.get(0).getUserSeq());
        System.out.println("UserName:" + userMsts.get(0).getUserName());
        
    }   
        
}       

ユーザーマスターテーブルからデータを取得するだけのコードになります。
まずはUserMstMapper をDIします。 例1のように、主キーで検索したい時はそれ専用に自動生成されたメソッドがあります。
そちらを使ってUserMst のエンティティを取得します、このあたりはどこのO/Rマッパーも同じかと思います。

例2のように条件を指定したい場合はExampleを使います。
これまでの客先でも特に使用していなかったのですが、今回すこしさわってみてかなり楽できることを発見しました。
ちなみに操作方法はまず、UserMstExample のインスタンスを作り、
userMstExample.createCriteria().andUserAccountEqualTo("user01");のようにアカウントに対する検索条件を追加します。
このインスタンスをselectByExampleのメソッドの引数に渡すと List が返ってきます。
なかなからくちんですね、もっと前に気づくべきでした😊

気をつけること

ひとつだけ気をつけることがあります。
このまま実行すると起動時にエラーが出ます。
f:id:tetsufuru:20191019122945p:plain

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.example.demo.service.rest.MyBatisTestRestService required a bean of type 'com.example.demo.mybatis.UserMstMapper' that could not be found.

Action:

Consider defining a bean of type 'com.example.demo.mybatis.UserMstMapper' in your configuration.

上記のメッセージは昔も出ていた気がします。
どうやって動くようにしていたかもう忘れてしまいましたので、 ネットでしらべて下記のようにアプリケーションの起動メソッドに@MapperScanを追加しました。
f:id:tetsufuru:20191019123341p:plain

参考サイト:https://qiita.com/matyahiko2831/items/d32039aef5f508408b42

動かしてみる

ではコントローラ経由で先ほどのサービスを動かしてみます。
コントローラの呼び出しは Postman を使います。
f:id:tetsufuru:20191019124126p:plain

下のような感じでユーザマスタに登録したデータを検索できました。
ログが出ています。
これで無事動くようになりました。
f:id:tetsufuru:20191019124238p:plain

感想


今回はMyBatisの導入について書きました。
MyBatis Generatorのことなど、わたし絶対にすぐ忘れるので書きとめてみました。

ふるっと思われた方もいらっしゃると思いますが、ご勘弁ください。
Spring JPAjOOQなど最近のもわからなくはないのですが、 SQLをゴリゴリ書きたいわたしにはやはりMyBatisが使いやすそうです。

そろそろ秋ですね、みなさま風邪をひかないようご注意ください。

それではまた😎

✨ふくてんの勉強会に参加しました - Fukuoka.NET Conf 2019

こんにちはふるてつです。

先日「ふくてん」開催の「Fukuoka.NET Conf 2019」に参加してきました。
関東で台風がひどかった日でしたが、福岡はそれほどはなく普通に参加できました。
その時の様子を少し書いてみました。
fukuten.connpass.com

勉強会の様子

会場は日本マイクロソフト九州支店でした。
中州のそこそこど真ん中にあります。
f:id:tetsufuru:20191014020900p:plain:w300f:id:tetsufuru:20191014020916p:plain:w300

参加者は30~40人というところでしょうか。
結構集まっている感じです。
f:id:tetsufuru:20191014113800p:plain

今日の内容

7名の方が登壇され、あと4名の方がLTと、もりだくさんな内容でした。

内容 登壇者
Welcome to .NET Conf 2019! - Special Message from Scott Hanselman (録画) Mr Scott Hanselman
.NET の今と今後に思うこと(リモートセッション) 井上さん
忙しい人のための .NET Conf 2019 まとめ 松村さん
.NET Core 3.0でBlazorを使用したフルスタックC# Webアプリの構築 ジョニーさん
.NET Core 3.0 for Windows Desktop 石橋さん
脆弱性診断することについて語るときに僕の語ること 大木さん
Hosting meets .NET dojinekoさん
LT枠 登壇者
Friendlyでシステムテストを自動化した話 宇津木さん
今やりたい!Web Appsで.NET Core 3.0を使う 加藤さん
AribabaクラウドFunctionComputeで.NET Coreが使える話 知念さん
院内Kubernetesのお話し 山之内さん

登壇の様子

基本的に写真を上手に取れた方のおはなしをのせてました。

● Mr Scott Hanselman の「Welcome to .NET Conf 2019!」
f:id:tetsufuru:20191014015925p:plain

今回は動画にて参加とのことでした。 大分偉い方のようですね。でもわたし英語はちと苦手で若干困りました… .Net Coreの今後の予定を話されていました、多分。

マイクロソフトの井上さんで「.NET の今と今後に思うこと」 f:id:tetsufuru:20191014020344p:plain
今度はリモートセッションでした。
チャックさんと呼ばれている方ですね、わたしも数回ですがお話を聞いたことがあります。
お話の途中でmsdnなど少し懐かしいフレーズが出たのが良かったですねぇ。

● オープニングでも登場された松村さんで「忙しい人のための .NET Conf 2019 まとめ」
Microsoft MVPらしい分かりやすいお話でした。 f:id:tetsufuru:20191014121847p:plain

● ジョニーさんの「.NET Core 3.0でBlazorを使用したフルスタックC# Webアプリの構築」 f:id:tetsufuru:20191014121624p:plain フロントエンドの話が出てきました。
わたしはいまAngular派ですけどもたいへん興味が沸きました。

● 石橋さんの「.NET Core 3.0 for Windows Desktop」
f:id:tetsufuru:20191014122017p:plain
わたし前の現場では、ばりばりWindows Form使ってました、2年くらい前なのでわりと最近の事ですね。
あのソースはいまどうなってますかねぇ、多分移行に手間がかかるだろうな🤢と思いました。

上記以外の登壇については熱中するあまり写真など取れませんでしたすみません、でも皆さん面白い発表だったと思います。

グッズ・おみやげ

ふくてんさんの勉強会はグッズが結構充実しています。
ステッカー、マグカップ、ストロー
f:id:tetsufuru:20191014123129p:plain:w300f:id:tetsufuru:20191014123331p:plain:w300
わたしはステッカーと、オレンジ色のマグカップをいただきました。

f:id:tetsufuru:20191014123500p:plain
こちらは.NET Coreの本。
じゃんけんで勝ちいただきました😊

今日の感想


以上、「ふくてん」さんの勉強会の様子でした。
開催者の方々、楽しい時間をありがとうございました。

現在の客先でわたしはSpring Bootを使っていますので少しMicrosoft製品とは離れてしまいました。
しかしまたそのうち客先が変わることもあるので、また使う時にそなえて最新情報は取り入れていこうと思っています。
あと、いただいた本は時間を取ってちゃんと使いたいと思います。

では、勉強会に参加された皆様お疲れさまでした。