Bot Framework と Microsoft Graph で DevOps その 10 : フォームフロー入門編
前回はダイアログ応用編として DialogPrompt の機能など紹介しました。今回はフォームフロー (FormFlow) 入門編をお届けします。
概要
前回 DialogPrompt を利用して、イベント作成に必要な情報を集めました。しかしフォームフローはこれをさらに簡単にしてくれます。フォームフローはクラスの定義を元に、適切なダイアログを作ってくれます。百読は一コードに如かずということで早速。
フォームフローを利用したイベントの作成
モデルの作成
フォームフローはモデルベースであるため、モデルを作ります。Microsoft.Graph.Event クラスをそのままモデルに使いたいんですが、Serializable 属性ないんですよね。あと不要なフィールドなどもあるので、新規作成を。
1. Visual Studio でボットアプリプロジェクトに Models フォルダを追加。
2. Models フォルダ内に、OutlookEvent.cs を追加し、以下と差し替え。Serializable 属性忘れないように。
using System;
namespace O365Bot.Models
{
[Serializable]
public class OutlookEvent
{
public string Subject { get; set; }
public string Description { get; set; }
public DateTime Start { get; set; }
public bool IsAllDay { get; set; }
public double Hours { get; set; }
}
}
CreateEventDialog.cs の変更
PromptDialog によるチェーンではなく、フォームフローを直接利用するようにします。以下のコードに差し替え。コード短くなりましたね。
using Autofac;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;
using Microsoft.Graph;
using O365Bot.Models;
using O365Bot.Services;
using System;
using System.Threading.Tasks;
namespace O365Bot.Dialogs
{
[Serializable]
public class CreateEventDialog : IDialog<bool> // このダイアログが完了時に返す型
{
public async Task StartAsync(IDialogContext context)
{
// FormFlow でダイアログを作成して、呼び出し。
var outlookEventFormDialog = FormDialog.FromForm(this.BuildOutlookEventForm, FormOptions.PromptInStart);
context.Call(outlookEventFormDialog, this.ResumeAfterDialog);
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<OutlookEvent> result)
{
await context.PostAsync("イベントを作成しました。");
// ダイアログの完了を宣言
context.Done(true);
}
private IForm<OutlookEvent> BuildOutlookEventForm()
{
OnCompletionAsyncDelegate<OutlookEvent> processOutlookEventCreate = async (context, state) =>
{
using (var scope = WebApiApplication.Container.BeginLifetimeScope())
{
IEventService service = scope.Resolve<IEventService>(new TypedParameter(typeof(IDialogContext), context));
// TimeZone は https://graph.microsoft.com/beta/me/mailboxSettings で取得可能だがここでは一旦ハードコード
Event @event = new Event()
{
Subject = state.Subject,
Start = new DateTimeTimeZone() { DateTime = state.Start.ToString(), TimeZone = "Tokyo Standard Time" },
IsAllDay = state.IsAllDay,
End = state.IsAllDay ? null : new DateTimeTimeZone() { DateTime = state.Start.AddHours(state.Hours).ToString(), TimeZone = "Tokyo Standard Time" },
Body = new ItemBody() { Content = state.Description, ContentType = BodyType.Text }
};
await service.CreateEvent(@event);
}
};
return new FormBuilder<OutlookEvent>()
.Message("イベントを作成します。")
.AddRemainingFields() // すべてのフィールドを処理対象として追加
.OnCompletion(processOutlookEventCreate)
.Build();
}
}
}
エミュレーターによる検証
エミュレータで実行すると、以下のような感じになります。
色々文言や質問の順番がきれいではありませんが、動くのは動きますね。ではこの辺直してみましょう。
表示の改善
モデルの変更
ユーザーに対する質問や順番はモデルをいじることで変更できます。モデルをいじらない方法は次回。
モデルを以下のように変更します。属性ですべてを制御できます。また {||} のようなパターンも使えます。使えるパターンはこちら。
using Microsoft.Bot.Builder.FormFlow;
using System;
namespace O365Bot.Models
{
[Serializable]
public class OutlookEvent
{
[Prompt("件名は?")]
public string Subject { get; set; }
[Prompt("詳細は?")]
public string Description { get; set; }
[Prompt("いつから?yyyy/MM/dd HH:mm 形式で入力してください。")]
public DateTime Start { get; set; }
[Prompt("終日イベント?{||}")]
public bool IsAllDay { get; set; }
[Prompt("何時間?")]
public double Hours { get; set; }
}
}
エミュレーターによる検証
前回と同じ感じになりました。
チェックインしてテストが通るかも確認してください。(この時点では失敗するテストがあるんですが、解決は次回)
まとめ
フォームフロー簡単すぎて拍子抜けしますが、次回は応用編ということでより高度な処理について紹介します。