FactoryをつかってAlexaの処理をDRYに書く

APIを呼び出す系のスキルなどでは、権限確認などの処理をどのハンドラーにも書かないといけないということがあります。 この例では、アカウントリンクのアクセストークンの有無を確認し、存在しない場合にアカウントリンクを促す処理 […]

広告ここから
広告ここまで

目次

    APIを呼び出す系のスキルなどでは、権限確認などの処理をどのハンドラーにも書かないといけないということがあります。

    
    const ListMessageIntentHandler: RequestHandler = {
      canHandle(handlerInput) {
        return isMatchedIntent(handlerInput, 'ListMessageIntent');
      },
      async handle(handlerInput) {
        const token = Alexa.getAccountLinkingAccessToken(handlerInput.requestEnvelope)
        if (!token) {
          return handlerInput.responseBuilder
            .speak('Please link your Alexa account to AAA account.')
            .withLinkAccountCard()
            .getResponse();
        }
        const client = new SomeAPI({token})
        const items = await client.messages()
        const message = items.member.map(item => item.name).join(' and ')
        return handlerInput.responseBuilder
          .speak('This is your messages. ' + message)
          .reprompt('What is next?')
          .getResponse()
      },
    };
    
    const ListItemIntentHandler: RequestHandler = {
      canHandle(handlerInput) {
        return isMatchedIntent(handlerInput, 'ListItemIntent');
      },
      async handle(handlerInput) {
        const token = Alexa.getAccountLinkingAccessToken(handlerInput.requestEnvelope)
        if (!token) {
          return handlerInput.responseBuilder
            .speak('Please link your Alexa account to AAA account.')
            .withLinkAccountCard()
            .getResponse();
        }
        const client = new SomeAPI({token})
        const items = await client.list()
        const message = items.member.map(item => item.name).join(' and ')
        return handlerInput.responseBuilder
          .speak('This is your items. ' + message)
          .reprompt('What is next?')
          .getResponse()
      },
    };

    この例では、アカウントリンクのアクセストークンの有無を確認し、存在しない場合にアカウントリンクを促す処理が重複しています。

    インテントの数が増えると、これを毎回書くのが煩雑です。

    そこでFactoryパターンで共通化してみましょう。

    FactoryでRequestHandlerを作る

    Factoryパターンで、必要なRequestHandlerを作ってもらう形にします。まずはFactoryを用意しましょう。

    class SomeServiceRequestHandlerFactory {
      public static create(intentName: string, handle: (token: string, handlerInput: Alexa.HandlerInput) => Promise<Response> ): RequestHandler {
        return {
          canHandle(handlerInput: Alexa.HandlerInput) {
            return isMatchedIntent(handlerInput, intentName)
          },
          async handle(handlerInput) {
          const token = Alexa.getAccountLinkingAccessToken(handlerInput.requestEnvelope)
            if (!token) {
              return handlerInput.responseBuilder
                .speak('Please link your Alexa account to AAA account.')
                .withLinkAccountCard()
                .getResponse();
            }
            return handle(token, handlerInput)
          }
        }
      }
    }

    あとは作りたいReqestHandlerをこのFactoryで作るだけです。

    
    const ListMessageIntentHandler: RequestHandler =  SomeServiceRequestHandlerFactory.create(
      'ListItemIntent',
      async (token, handlerInput) => {
        const client = new SomeAPI({token})
        const items = await client.messages()
        const message = items.member.map(item => item.name).join(' and ')
        return handlerInput.responseBuilder
          .speak('This is your messages. ' + message)
          .reprompt('What is next?')
          .getResponse()
      },
    };
    
    const ListItemIntentHandler: RequestHandler = SomeServiceRequestHandlerFactory.create(
      'ListItemIntent',
      async (token, handlerInput) => {
        const client = new SomeAPI({token})
        const items = await client.list()
        const message = items.member.map(item => item.name).join(' and ')
        return handlerInput.responseBuilder
          .speak('This is your items. ' + message)
          .reprompt('What is next?')
          .getResponse()
      }
    )

    かなり見通しがよくなりましたね。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark