はまったりひらめいたり…とか…

Angularや.NETやAzureやその他色々。

NLogを.Net Standard/Coreで使用する

はじめに

.Net Core等は下記バージョンでお送りします。

  • netstandard: 2.0
  • netcoreapp: 2.2
  • AspNetCore: 2.2.0
  • NLog: 4.6.3

あらまし

NLogを使うとかは今更ではあるのですが

.Net CoreのExeアプリケーションからASP.net WebAPIアプリケーションで

NLogを使用する機会があったので、学習ついでの備忘録な感じのトピックです。

NLog.configの設定を外に出す

基本的な使い方は本家のGitHubやググったら山程でてくるので割愛…

NLogのログ出力の設定は、nlog.configXML形式ファイルに記述していく感じです。

が、これはいまいち好きじゃない。

と、いうのもASPにしてもCoreの普通のアプリにしてもjsonファイルにアプリケーション設定を記述していますし

あちこちのファイルに設定内容が散らばっているのも少し邪魔くさい。

ASP.netに関してはWebAppsがもっているアプリケーション設定で出力先をいろいろ設定できるようにすれば

CI/CD側の負担も軽くなるんでは?と思ったわけです。

(nlog.configのようなXMLファイルの設定もWebAppsの設定上でできるのであればいいのですが…ない?ですよね?)

NLog.configの構成

まずはXMLで設定されるNLogの構成を見てみます。

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets>
    <target name="console" xsi:type="Console" />
  </targets>
  <rules>
    <logger name="*" minlevel="Info" writeTo="console" />
  </rules>
</nlog>

これはLogの内容をConsoleに吐き出す設定ですが、target で「何に対して」ログ出力するか指定し

rule で 'target' に対して出力するログの内容を指定しています。

この場合、「Infoレベルからのログを Console に出力する」という設定になっているわけですね。

NLogのTarget/Ruleの関係は、下記サイトが参考になりました。

NLog使い方メモ - マコーの日記

NLogの設定を動的に指定する

こちらのサイトを参考にしました。

NLogをプログラマブルに初期化し動的に構成変更する - M12i.

NLog.Target 名前空間ConsoleTarget というClassが存在します。

NLog/ConsoleTarget.cs at dev · NLog/NLog · GitHub

他にも FileTargetDatabaseTarget が存在します。

Classにあるプロパティを見てみると、LayoutConnectionStringなどXMLで設定するプロパティが存在するのが確認できます。

XMLの設定を確認しながら、TargetClassの同名プロパティに値を設定していく…という方法でNLogの設定ができそうです。

で、出来上がったのが下記です。

public static void ConsoleLogInit()
{
    var conf = LogManager.Configuration;
    var console = new ConsoleTarget("console"); // consoleターゲットを生成
    console.Layout = LogLayout;  // アウトプットフォーマットレイアウトを設定
    conf.AddTarget(console);  // NLogの設定に生成したターゲット情報を追加
    conf.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, console));  // consoleターゲットを使用するルールを追加
    LogManager.Configuration = conf;  // NLogの設定に反映
}

あとは同じように設定するだけ

TargetのClassにたいしてどのようなAPIが存在してどのような引数が求められているのかは

上記のGitHubソースコードドキュメントから抑えることができるので

ファイルに出力する場合やDBに出力する場合も同じように設定していくだけです。

ファイルに出力する場合はファイル名称やエンコードを指定するAPIが追加されていたりします。

public static void WriteLogToFileInit(string filePath, LogLevel targetLogLevel)
{
    var conf = LogManager.Configuration;
    var file = new FileTarget("file");
    file.Encoding = Encoding.UTF8;
    file.FileName = filePath;
    file.Layout = LogLayout;
    conf.AddTarget(file);
    conf.LoggingRules.Add(new LoggingRule("*", targetLogLevel, file));
    LogManager.Configuration = conf;
}

Databaseはちょっと変わり種でDatabaseParameterInfoのインスタンスにLayoutを設定しないといけません。

public static void WriteLogToSqlDatabaseInit(string connectionString, LogLevel targetLogLevel)
{
    var conf = LogManager.Configuration;

    var dbtarget = new DatabaseTarget();
    dbtarget.ConnectionString = connectionString;
    dbtarget.Name = "dbtarget";
    dbtarget.DBProvider = "System.Data.SqlClient";
    dbtarget.CommandText = "Insert Into LoggingTable("
            + "Logged,"
            + ") values ("
            + "@logged,"
            + ")";
    var loggedParam = new DatabaseParameterInfo();
    loggedParam.Name = "@logged";
    loggedParam.Layout = "${date}";
    dbtarget.Parameters.Add(loggedParam);

    conf.AddTarget(dbtarget);
    conf.LoggingRules.Add(new LoggingRule("*", targetLogLevel, dbtarget));
    LogManager.Configuration = conf;
}

外部からNLogの設定を行う!

と、いうわけでソースコード上でNLogの設定が十二分に行えることがわかりました。

あとは、ASP.net WebAPIなどのappsettings.jsonなどに適当な設定を作ってあげればいいだけです。

適当に↓な感じでaapsettings.jsonを作って

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    },
    "OutputToLogFile": {
      "FilePath": "logfile.txt",
      "LogLevel": "Error"
    },
    "OutputToDatabase": {
      "ConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=Logging;Integrated Security=True;",
      "LogLevel": "Info"
    }
  },
  "AllowedHosts": "*"
}

Startup.cs あたりで設定ファイルを読み込んでNlogを設定すればいい感じかなと思います。

private void NLogSettings()
{
    Logging.LoggingSettings.ConsoleLogInit();
    var fileName = this.Configuration["Logging:OutputToLogFile:FilePath"];
    var logLevel = this.Configuration["Logging:OutputToLogFile:LogLevel"];
    // NLogの設定を行う~~
}

これでWebAppsのアプリケーション設定への設定のみでログ出力先の挿げ替えなどが簡単に行えるようになりました。

終わりに

今回作成したモロモロのデモは下記リポジトリになります。

ASP.net WebAPIの ActionFilterAttributeExceptionFilterAttribute 使ったり

EntityFrameworkの実行SQLをログ出力したりする実験コードも含んでたりします。

github.com

「簡単な」入力状態復帰機能をAngularのReactiveFormsで実装した話

はじめに

Angularは下記のバージョンでお送りします。

@angular/cli: 7.3.9

なにがしたかったか

  • 認証にAzure ADを使用している関係で、定期的に(1時間立ち上げっぱなしで1回程度)ページがリフレッシュされる
  • 入力内容が消えちゃうのもアレなので入力状態を保存しておいて復帰させたい。
  • 巷の入力状態復帰ライブラリはCookieやlocalstorageに保存するものがほとんど
  • じゃあライブラリ使わなくてもReactiveForms使ってたら余裕で実装できるかも?

というわけで実装してみよう…というあらましです。

行うこと

  1. ReactiveFormsのvalueChangesイベントをサブスクライブします。
  2. ReactiveFormsはデータを構造体でもっているので、そのデータをJSONにしてlocalstorageに保存します。
  3. ComponentがInitializeされた段階でlocalstorageにデータが存在する場合はpatchValueで入力されていたデータを復帰します。

以上。

極端な話、下記ソースコードだけで実現できるということですね。

(もちろん格納データの存在チェックやらは必要なのでこれ+もろもろは必要ですが!)

ngOnInit() {

    // 起動時にlocalstorageの値をFormsに反映
    const stVal = JSON.parse(localStorage.getItem('hogehoge'));
    this.sampleInputFormGroup.patchValue(stVal);

    this.sampleInputFormGroup.valueChanges.subscribe(value => {
        // 変更内容をlocalstorageに保存
        const valSt = JSON.stringify(value);
        localStorage.setItem('hogehoge', valSt)
    });

}

結果↓のGifのように動きます。

f:id:TakasDev:20190524223000g:plain

タブを閉じて再度開いても

入力状態が保持されている事がわかると思います。

まとめ

ReactiveFormsは構造体の状態で値の受け渡しができるので

ライブラリなしでも入力状態復帰処理は簡単に実装できますよ!ということがわかりました。

上の図で動いているソースのコードは下記になります。

Component - Service間をRxJSでつなげたりとか

ほんのちょっとだけ複雑になっています。

github.com

ごった煮のソースの一部で申し訳ないですが。。。 ><

KarmaやJestのカバレッジをAzureDevOpsパイプラインで収集してみた

昨日、Azure DevOps Tokyo, Japan 2nd impactに参加しました。

jazug.connpass.com

その時にAzure DevOps パイプラインでのカバレッジレポートの収集の話になり

自分が今使っているAngularのプロジェクトでも、カバレッジレポートが収集できるか試してみました。

現在、自分がAngularで行っているユニットテストは、KarmaとJestの2パターンあるので

その両方で試してみた結果となります。

はじめに

ちと古いですが、昔JestのテストとCIを組んだ構成を利用したので

Angular Cli:7.0.5で試した結果となります。

パイプラインでのカバレッジレポートのPublish

ユニットテストの結果出力同様、テストバレッジの結果出力も、専用のタスクがあるようです。

タスク名は「Publish Code Coverage Results」となっています。

パイプライン構築GUIで見てみると、下図のような設定画面となっているようです。

f:id:TakasDev:20190119232434p:plain

YAMLの設定については下記サイトに記載されています。

Publish Code Coverage Results task - Azure Pipelines | Microsoft Docs

ユニットテスト同様、結果出力されたXMLファイル指定するようです。

XMLのフォーマットとしてCobertura か JaCoCoのどちらかが指定されています。

ざっと調べてみたところ、JestもKarmaもCobertura フォーマットのXMLを吐けるようなので

Cobertura フォーマットのカバレッジレポートをパイプラインでPublishしようと思います。

Jest

Jest自身がCoberturaフォーマットのカバレッジレポートを出力する機能を持っているようなので必要最低限の変更で済みます。

AzureDevOps + Angular + Jestのテストパイプラインについては、以前の記事を参考にしてください

package.json

  "test:watch": "jest --watch",
- "test:ci": "jest --reporters=jest-junit",
+ "test:ci": "jest --reporters=jest-junit --coverage",
   "reporters": [
     "default",
     "jest-junit"
   ],
+ "coverageReporters": [
+   "text",
+   "html",
+   "cobertura"
+ ]

上記の設定で、npm run test:ciを実行すると、テスト実行時にcoverageディレクトリにカバレッジレポートが出力されます。

f:id:TakasDev:20190119234741p:plain

Karma

Karmaも簡単な変更でカバレッジレポートをcoberturaフォーマットで出力できるようになります。

angular.json

  "karmaConfig": "src/karma.conf.js",
+ "codeCoverage": true,
  "styles": [
     "./node_modules/@angular/material/prebuilt-themes/purple-green.css",
     "src/styles.scss"

karma.conf.js

  coverageIstanbulReporter: {
    dir: require('path').join(__dirname, '../coverage'),
-   reports: ['html', 'lcovonly', 'text-summary'],
+   reports: ['html', 'lcovonly', 'text-summary', 'cobertura'],
    fixWebpackSourcePaths: true
  },

karma側も同様の構成でカバレッジの結果が出力されます。

f:id:TakasDev:20190120000455p:plain

Azure DevOpsパイプライン

Azure DevOps側に戻ってきました。

出力したカバレッジレポートのXMLとレポートHTMLを取得するタスクを作成します。

KarmaもJestも、同じ名前のディレクトリに同じ名前のファイルでXML出力されているので同様の設定となります。

タスクのyamlは下記の通りです。

yaml

- task: PublishCodeCoverageResults@1
  displayName: 'Publish code coverage from $(System.DefaultWorkingDirectory)/**/cobertura-coverage.xml'
  inputs:
    codeCoverageTool: Cobertura

    summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/cobertura-coverage.xml'

    reportDirectory: '$(System.DefaultWorkingDirectory)/**/coverage'
  • codeCoverageTool : 今回Coberturaフォーマットを使用したのでCoberturaを指定します。
  • summaryFileLocation : 出力されたXMLファイルを指定します。
  • reportDirectory : カバレッジのHTMLレポートのディレクトリを指定できます。

結果

CIログのSummaryタブで全体の結果を確認できます。

f:id:TakasDev:20190120001542p:plain

CodeCoverageタブでは出力されたHTMLレポートが確認できます

f:id:TakasDev:20190120001748p:plain

デザインが統一されていないため、すこぶる見にくいです…

ダークテーマで見にくささらにドン。といった感じですね…

最後に

Angularに限らずKarmaやJestのテストフレームワークを使用している場合

コードカバレッジの結果をAzureDevOpsのCIでレポート出力するのはとても簡単だということが分かりました。

これでより良いユニットテストCIライフが送れそうですね!

AngularでQR/バーコードを使用するInputコントロールを作る

12/1 修正しました。

はじめに

今回のお話は、下記バージョンでお送りします

AngularCLI : 7.0.4

↓みたいなコントロールを作成していきます。

f:id:TakasDev:20181123231755g:plain

f:id:TakasDev:20181123232143g:plain

テキストボックスがあり

フォーカスが当たった際に、ソフトウェアキーボードではなくQRを読み込むカメラが起動し

カメラでQRが読み込めた場合

フォーカスが当たっていたテキストボックスに、QRから読み取った値を投入する。

といった動作です。

QR/バーコード読み込み

以前の記事でQRコードを読み取るために、@zxing/ngx-scanner を使用しました。

今回は、そのライブラリからForkされた、@innotec/ngx-scanner を使用します。

@innotec/ngx-scanner - npm

npm install @innotec/ngx-scanner --save

@zxing/ngx-scannerの@Inputにはformatsが追加されており、CODE39等のバーコードのFormatを指定することで

バーコードのデータも読み込むことができるようになります。

Formatの指定は、@zxing/libraryEnumで管理されている、BarcodeFormat の配列を使用します。

Selectorはzxingのngx-scannerと同様なので、下記のようにFormatを指定する部分のみが変わる感じです

<zxing-scanner #scanner
  [formats]="[BarcodeFormat.QR_CODE, BarcodeFormat.CODE_39]">
</zxing-scanner>

ngx-scannerの使い方は、以前書いた記事を参考にしていただければと思います。

AngularでQRする - はまったりひらめいたり…とか…

Angular Material BottomSheet

画面下からにゅっと飛び出てくるコントロールを作るために

AngularMaterialのBottomSheetコンポーネントを使用しました。

Angular Material

準備

まずは、Materialを使用するために、お決まりの

ng add @angular/materialを行っておきます。

@NgModuleでBottomSheeetのモジュールMatBottomSheetModuleをインポートしておきます。

コンポーネントからの使用

constructorでMatBottomSheetのDIを行うことで、BottomSheetの処理が使用可能になります。

コンポーネントから、BottomSheetのOpen() を実行すればBottomSheetが表示されます。

その際、BottomSheet内に表示するComponentを指定することが可能です。

≪MatBottomSheet≫.open(SheetBodyComponent);

この時BottomSheetに表示されるComponentは、表示されるタイミングでインスタンス化されるようなので

LazyLoadするComponentの時同様、NgModuleのEntryComponentに加えてあげる必要があるようです。

@NgModule({
...
  entryComponents: [
    SheetBodyComponent
  ],
...
})
export class AppModule { }

サービスの注入

QR/バーコードが読み込まれたタイミングで、親コンポーネントがその結果を引っ掛けたいです

ただ、BottomSheet内のComponentは、EntryComponentなので、@Outputあたりで引っ掛けるのも難しそうです。

なのでDIするClassを作成し、その中のSubjectなプロパティを介して

BottomSheet内Componentと、親Componentでデータのやり取りを行う方法をとろうと思います。

コンポーネント

// BottomSheetをOpen
const res = ≪MatBottomSheet≫.open(SheetBodyComponent);

≪DI Class≫.≪Subjectプロパティ≫.subscribe(x => {
  // QR/バーコードがScan出来た場合
  // InputElementに対して取得したQR結果を突っ込む
  // ElementRefを使用しInputコントロールを取得する
  const con = <HTMLInputElement>≪ElementRef≫.nativeElement.querySelector('input');
  con.value = x;
  // BottomSheetを閉じる
  res.dismiss();
});

BottomSheet内コンポーネント

handleQrCodeResult(resultString: string) {
  // QRが読み込めた時の処理。サービスのSubjectプロパティに値を流す
  ≪DI Class≫.scanSuccess.next(resultString);
}

スクリプト内で特に指定しない限り、BottomSheetはEscキーなり、BottomSheet外をClickするなりしないと閉じないので

値が読み込めた段階で、bottomsheetのdismiss()を使用して用がすんだら閉じてあげるのが良いかと思います。

bottomsheetのafterDismissed().subscribe()で、BottomSheetが閉じた時を引っ掛けれるので

BottomSheet閉じた際に何か処理をしたい場合は、そいつを使うと楽にいろいろできそうです。

これで、親ComponentとBottomSheetのComponentでデータのやり取りが可能になりました。

結果

冒頭のGifの通り、QR/バーコードを使用したInputコントロールが作成できました。

おわりに

ソフトウェアキーボードみたいな、フォーカスあてると下からニュッと入力インターフェースが出てくるコントロールを作りたいという思いでBottomSheetを使用しました。

今回はQR/バーコードの読み取りで使用しましたが

Canvasあたりを使用すれば、手書きの入力コントロールも、OSのインターフェースっぽく表現できるかもしれませんね。

今回作成したものは、下記リポジトリで管理しています。

サンプルで作ったもののごった煮ですがご容赦ください。

github.com

バーコードとQRの生成は下記のサイトを使用しました。

バーコード

barcode.tec-it.com

QR

m.qrqrq.com

Azure DevOpsとBlob StorageでClickOnceアプリケーションのCI/CDできるか「雑に」試してみた

はじめに

れがしぃなWindowsFormsなプロジェクトをAzure DevOps(ADO)でCI/CDできるか「雑に」試してみました。

exeをどこかに吐き出すだけでは面白くないので

Azure Blob Storage上にClickOnceアプリケーションをデリバリして

Web Apps上のリンクからそのアプリケーションを使用する。

みたいな形で構成していこうと思います。

「できるかな?」程度なので、超雑な構成になっています。

証明書周りやCORS周りなんかは、もうちょっと検証が必要と思います。。。

最終的に↓な感じで動きます。

f:id:TakasDev:20181118193605g:plain

Windows Formsプロジェクト

まずはWindowsFormsプロジェクトの作成です。

といっても、アプリケーション自体はそこまで重要ではないので

WindowsFormsの画面を表示させるだけの機能のものです。

アプリケーションの発行する際にClickeOnceの署名等も行うよう設定しておきます。証明書はVisualStudioで作成されるテスト証明書をそのまま使用します。

f:id:TakasDev:20181118182610p:plain

ADO CI/CDパイプライン

VisualStudioBuildタスク

ADOパイプラインのVisualStudioBuildタスクはデフォルトで、publishしてくれないので、タスクの内容を少しいじります

MSBuild:Arguments/target:publish を指定するだけです。

f:id:TakasDev:20181118182713p:plain

PowerShellタスク

WinFormsのClickOnceで証明書を使用しているため、Buildを行う際にはマシンの証明書ストアに証明書がインストールされている必要があります。

PowerShellタスクを使用して、ビルドマシンに証明書をインストールしてあげます。

証明書ファイルはめっちゃ雑ですが

ソリューションに加えられたpfxファイルをADOにあげちゃってます

f:id:TakasDev:20181118182837p:plain

$pfxpath = 'pfxファイル'
$password = '証明書パスワード'

Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($pfxpath, $password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]"PersistKeySet")
$store = new-object system.security.cryptography.X509Certificates.X509Store -argumentlist "MY", CurrentUser
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]"ReadWrite")
$store.Add($cert)
$store.Close()

f:id:TakasDev:20181118182943p:plain

Azure File Copyタスク

ClickOnceアプリケーションは bin の中に生成されるので

binディレクトリの中のファイルをFile CopyタスクでBlobストレージにアップロードします。

f:id:TakasDev:20181118191122p:plain

基本的にAzureのBlob Storageとリンクされます。なので迷わず設定できます。

結果

f:id:TakasDev:20181118191451p:plain

指定したコンテナに.application他生成されたファイルがコピーされました。

Blob Storage他の雑な設定変更

ここからかなり雑です。

アセンブリの設定変更

オレオレ以前な証明書を使用しているので、*.application にアクセスしてもファイルの起動が行なえません。

f:id:TakasDev:20181118192258p:plain

Let'sやらOpenSSLを使用してあげれば回避できるとは思うのですが

今回は、コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\Security\TrustManager\PromptingLevel の設定をすべてenabledに変更して回避しました。

BlobStorageのアクセスポリシー変更

当初、SASトークン付きのURLでapplicationファイルにアクセスしていたのですが

アプリケーションのインストールを行う際、当然 .manifest などの他のファイルもBlobStorage経由で取得しようとします。

しかし、アプリケーションのインストール時は、インストーラSASトークン付きのURLに対してアクセスしてくれないため、404が発生し、インストール時にエラーとなってしまいます。

BlobStorageでCORSを設定してみたのですが、そもそもローカルにインストールされたインストーラからのアクセスなので、CORSも効かない…

ので、アクセスポリシーをユッルユルに設定しアクセスできるようにしました。

f:id:TakasDev:20181118192830p:plain

雑に設定した結果

.application へのLinkを貼っただけのHtmlをアップし動作させてみました。

結果は記事の冒頭に貼った通りの結果でClickOnceのおなじみの動きとなっています。

おわりに

BlobStorageにアップしたClickOnceアプリケーションは使用できそうです。

ただ、さすがにAnonymousで制限なくアクセス可能。という状態を避けたい場合はもうすこし考慮が必要かもしれません。

アプリケーションへのエントリがWebからのみ。ということであれば

Web画面のCI/CDパイプラインでアプリケーションを混入した状態で、WebAppsにデプロイしちゃったほうが良いかもしれないですね。

Azure DevOpsでAngularのJestテストのCIをまわせるか試してみた

はじめに

Angularは下記Verisonでお届けします。

  • AngularCli:7.0.3

先日の、bitbank LT NightでみたJestがとても良い感じで使ってみたくなったので

現在使用しているCI環境のAzure DevOps(以降ADOと呼びます)で使用できるか確認してみました。

jestjs.io

Azure DevOps

お仕事で使用していますが、Publicなプロジェクトでも使用できます。

Publicなプロジェクトの場合(厳密にはOSSプロジェクトとのことですが)、ビルド時間を無制限に回せるようです。

Azure DevOps のご紹介 – Cloud and Server Product Japan Blog

WebからWindowsアプリ、Andorid/iOSもビルド可能となかなか最強味が強い環境です。

OneDriveみたいに、無制限→制限!のような未来を辿らなければいいと願っています。

Angularの下準備

まずはAngularでJestのテストができるようにします。

下記を参考にしました。

izifortune.com

ひとまず、ng new したプレーンな環境から作成しました。

上記の記事に記載されている通りの変更を行います。

  • パッケージのインストール
  • jestテストするためのスクリプトをpackage.jsonに追加
  • jestの設定ファイルのjest.tssrc ディレクトリに配置
  • tsconfig.spec.jsonの変更

あっさりとJestでテストが可能なところまで持っていくことができました。

f:id:TakasDev:20181111192325p:plain

結果レポートの出力

前の記事の内容の通り、ADOはjUnitXMLを食わせばレポート出力してくれるので

jestからjUnitのレポートを出力できるようにします。

パッケージのインストール

jUnit出力に使用するパッケージをインストールします。

npm install --save-dev jest-junit

レポート出力の設定

Jestコマンドでレポート出力されるよう、package.jsonの設定を変更していきます。

  "jest": {
...
    "reporters": [
      "default",
      "jest-junit"
    ]
  }

defaultが先程行ったコマンドラインで表示されるテスト

jest-junitjUnitXML形式で出力されるテストの設定です。

今回は、jest-junitのオプションに特に指定をしないので

rootディレクトリに「junit.xml」という名前で結果ファイルが出力されます。

テストコマンドも少し変更します。

今回の変更でテストレポートが複数パターンになったので

テスト内容によってどっちのレポートを使用するか指定しておきます。

package.jsonスクリプトを下記のように変更しました。

  "scripts": {
...
    "test": "jest --reporters=default",
    "test:ci": "jest --reporters=jest-junit",
...
  }

スクリプトコマンドの通り、npm run test:ciXMLが出力されます。

CIの構築

今回はGitHubリポジトリから、ソースを引っ張ってきてADOでCIします。

せっかくなので、Public環境のADOを使用しようと思います。

ADOの設定

GitHubリポジトリからソースを引っ張ってくるパイプラインを作ります。

f:id:TakasDev:20181111194540p:plain

f:id:TakasDev:20181111194659p:plain

NewPipelineでGitHubを選択するだけ。簡単ですね。

テンプレートはどうせあとからいじるので、Blankなものを選択します

パイプラインを作成したら、ソースのリポジトリのルートディレクトリに

パイプラインの設定ファイルazure-pipelines.ymlが追加されています。

パイプラインをYamlで作成

パイプラインができたら、Buildの設定を作っていきます。

ADOのCIは、デフォルトではルートディレクトリにある、azure-pipelines.yml の設定を参照し、CIのパイプラインが実行されます。

今回は、下記の感じで構成しました。

ルートディレクトリ配下にAngularのプロジェクトファイルがあるのでちと変則的ですが…

resources:
- repo: self
queue:
  name: Hosted VS2017
  demands: npm

npmインストール
steps:
- task: Npm@1
  displayName: 'npm install'
  inputs:
    workingDir: AngularWithJest

    verbose: false

npmテスト実行。CI用のテストを実行する。
- task: Npm@1
  displayName: 'npm run test'
  inputs:
    command: custom

    workingDir: AngularWithJest

    verbose: false

    customCommand: 'run test:ci'

出力されたXMLファイルをテスト結果として取得する
- task: PublishTestResults@2
  displayName: 'Publish Test Results **/junit.xml'
  inputs:
    testResultsFiles: '**/junit.xml'

  condition: always()

buildする。
- task: Npm@1
  displayName: 'npm build'
  inputs:
    command: custom

    workingDir: AngularWithJest

    verbose: false

    customCommand: 'run prod-build'

ADOのyamlについては、下記サイトが参考になります。

YAML schema - Azure Pipelines | Microsoft Docs

結果

あえて失敗するテストを混入した状態でCIを回しました。

下記の通りきちんとエラーの内容が出力されています。

f:id:TakasDev:20181111200640p:plain

エラーを修正し、再度CIを回すと、きちんと反映されていることが確認できます

f:id:TakasDev:20181111210118p:plain

最後に

そもそもJest使用できるかな?程度のものなので、まだまだ検証することは多いかなと思います。

Jestは全然触っていないので、これからいろいろ覚えたい感じです。

さて、今回作成したソースと環境は下記になります。

Publicな環境でAzure DevOpsプロジェクト作成したのでテスト結果も見る事が可能です。

GitHub:AngularXStudy

Azure DevOps: angularxstudy-ci

(僕の失敗の軌跡も見ることができるので少し恥ずいですが…)

AngularでQRする

はじめに

この記事は、下記のVersionでお送りします。

  • Angular CLI : 7.0.3

どんな風に動作する?

こんな感じです。ちょっと見えづらいですが

一番下部で数字をカウントしているのがQRコードの読み取り結果です。

QRは2秒毎に0からカウントアップしていき、秒数をQRコードに変換しています。

QRコードの読み取りは早い方ではないでしょうか?

f:id:TakasDev:20181101070256g:plain

AngularでQRコードを読む

使用するライブラリ

QRコードを読むライブラリとしては、ネイティブではZxingが有名だと思います。

Angular用のZxingライブラリのngx-scannerが開発されいますので

今回はそれを使用しようと思います。

github.com

注意

今、iOS12でQRを読み込んでくれないバグが存在するようです。

が、上のGifの通り動作していますね。

Angular6 + ngx-scannerの食い合わせが悪いのかもしれません。

(今回、Angularはv7を使いました)

次のリリースで解決するとアナウンスはされていますが、使用する際は注意が必要です。

IOS12 iphone/ipad · Issue #140 · zxing-js/ngx-scanner · GitHub

ngx-scannerの事始め

QRコードの読み取り機能自体は、非常に簡単に使用することができます。

npm i @zxing/ngx-scanner --saveして

Angularでは恒例の、ngModuleでインストールしたライブラリを読み込んでやってから

Htmlテンプレートで<zxing-scanner></zxing-scanner>だけで使用できます。

ただし、Webカメラを使用する必要があるため

それを使用できるようにするまでで、ひと手間必要です。

バイスで使用できるカメラを取得する

zxing-scanner コンポーネントで使用できるカメラを探してくれます。

結果は、コンポーネントcamerasFound ファンクションに

Obserbableで返却されます。

<zxingcomponent>.camerasFound.subscrive( devices => { hogehoge } )

引っ掛けて使用できるカメラデバイスを把握し

使用するカメラを選択する必要があります。

使用するカメラをコンポーネントdevice プロパティに食わせて

使用できる状態にする必要があります。

簡単に書くと下記な感じでしょうか?

    <zxingcomponent>.camerasFound.subscribe((devices: MediaDeviceInfo[]) => {
        <zxingcomponent>.device = <zxingcomponent>.getDeviceById(devices["index?"].deviceId);
    });

ただ、カメラデバイスを見つけた時のObservableのみでdeviceを設定すると

好きなカメラに変更できないので

<zxingcomponent>.device に食わせる値は変数にして

取得した devices を保持して変更できるようにしておいたほうが良いです。

プロパティに使用するカメラの情報を食わせると

カメラを使用してQRを読み込んでくれるようになります。簡単ですね。

読み取り結果を取得する

Scanした結果は、Zxingコンポーネントの 'scanSuccess' ファンクションに

Observableで返却されます。

<zxingcomponent>.scanSuccess().subscribe(result: string => { })

結果を引っ掛けるだけでOKです。

camerasFoundscanSuccess もEventEmitterで結果が返却されているようなので

<zxing-scanner (camerasFound)="hogehoge()" (scanSuccess)="fugafuga()" >...

としてもいいと思います。これも非常に簡単ですね。

AngularでQRコードを生成する

使用するライブラリ

ngx-scannerで紹介されていた ngx-kjua を使用します。

github.com

ngx-kjuaことはじめ

npm install ngx-kjua --save して、同様にngModuleでライブラリを読み込んでから

Htmlテンプレートで<ngx-kjua></ngx-kjua> で使用できます。

カメラ等のデバイスを使用しないため、特に前段階の準備をすることなく使用できます。

QRコードにしたい文字列かimageをコンポーネントのプロパティに下の感じで指定するだけで

QRコードが生成されます。超楽です。

<ngx-kjua [text]="'qrにしたい文字列'"></ngx-kjua>

さいごに

QRコードを用いたアプリケーションを比較的容易に作成することができます。

いろいろと利用できる範囲も広いですし、楽しい使い方もできると思います。

今回作成したWebApplicationは、下記レポジトリで管理してます。

github.com

Angularの勉強で使用したソースのごった煮ですが、ご容赦ください。