Qmonus Documents /
ApiFront /7.GUI開発チュートリアル

GUI開発チュートリアル

ここではSPAの開発とQmonus ApiFrontを用いたSPAホスティングについて解説します。

本章では下記をゴールイメージとしています。
Qmonus SDK LabからQmonus ApiFrontを用いてホスティングできるようになる。

  • 簡易SPAの完成
  • LabからApiFrontのデプロイ
  • ローカルGUIとLabでデプロイした環境のAPI疎通
  • E2E試験の作成

 

本チュートリアルはGUI開発経験のない方から対象としているため、ご自身の開発レベルに合わせて開発に必要な章をお読みください。
SPA開発経験者の方は3.[Qmonus SDK Lab]SPAのデプロイからの閲覧を推奨しています。

 

なお、本チュートリアル実行には

が前提となります。Qmonus SDK Labの設定やアカウントについてはご自身のプロダクト管理者に問い合わせください。

 

  • 用語の整理
  • 1. [ローカル]セットアップ
  • 2. [ローカル]Vueでの開発
  • 3. [Qmonus SDK Lab]SPAのデプロイ
  • 4. 起動パラメータの設定と反映
  • 5. ローカルとQmonus SDK Labの疎通
  • 6. FrontTesterを用いたGUI試験

  •  

    用語の整理

    以下、特に記述がない場合、Qmonus SDKのプロダクトは下記のように呼びます。

    Lab

    Qmonus SDK Labのこと。各開発環境を管理するプラットフォーム。
    ドキュメント:https://docs.sdk-lab.qmonus.net/overview/top.html

    SDK

    Qmonus SDKの開発フレームワークのこと。ソフトウェア開発を実施するための機能を提供。
    ドキュメント:https://docs.sdk.qmonus.net/

    Portal

    Qmonus SDK Portalのこと。SDKの開発用IDEの他、認証認可やモニタリングなど様々な機能を持つ総合開発環境。
    ドキュメント:https://developer.qmonus.net/document/frontal/top

    ApiFront

    Qmonus ApiFrontのこと。SPAのホスティングやプロキシーなどFront周りの機能を持つ軽量Webサーバ。
    ドキュメント:https://developer.qmonus.net/document/casval/top

    FrontTester

    Qmonus FrontTesterのこと。playwrightエンジンを搭載し、GUIのE2E試験をサポートする開発支援フレームワーク。
    ドキュメント:https://developer.qmonus.net/document/edwow/top

     

    1. [ローカル]セットアップ

    ローカル環境でSPAの作成を行います。 SPAは各々の開発ツールを使っていただいて問題ありません。 今回は初学者向けに一例としてVueを使用します。

    1-1. Vueのインストール

    今回はnpmでグローバルにvueをインストールしています。

    bash
    $ npm install -g @vue/cli

    Vueがインストールされているか確認します。今回は5.0.8で進めます。また、nodeは v18.13.0で開発しています。

    bash
    $ vue --version @vue/cli 5.0.8 $ node -v v18.13.0

     

    1-2. プロジェクト作成

    projectを作成します。GitHubにpushが可能な状態のディレクトリを使用ください。Vueは2系でも3系でも問題ありません。今回は3系にします。


    $ vue create client Vue CLI v5.0.8 ? Please pick a preset: Default ([Vue 3] babel, eslint)

    インストールが完了後、下記メッセージが確認できます。

    bash
    🎉 Successfully created project client. 👉 Get started with the following commands: $ cd client $ npm run serve

    この時ディレクトリは下記の構成であればprojectの作成は成功です。

    早速cdして起動してみましょう。

    bash
    $ cd client $ npm run serve > client@0.1.0 serve > vue-cli-service serve ...

    http://localhost:8080/にアクセスして下記画面が見えれば、プロジェクトの起動は成功です。

    Tips:
    今回のチュートリアルで使用したパッケージのバージョンは下記になります。チュートリアル時に動作確認に失敗する場合、バージョンを合わせて実施してください。

    json
    ./client/package.json { "name": "client", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "@mdi/font": "5.9.55", "axios": "^1.3.4", "body-parser": "^1.20.2", "core-js": "^3.8.3", "http": "^0.0.1-security", "roboto-fontface": "*", "vue": "^3.2.13", "vue-axios": "^3.5.2", "vuetify": "^3.0.0-beta.0", "vuex": "^4.0.2", "webfontloader": "^1.0.0" }, "devDependencies": { "@babel/core": "^7.12.16", "@babel/eslint-parser": "^7.12.16", "@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-eslint": "~5.0.0", "@vue/cli-service": "~5.0.0", "eslint": "^7.32.0", "eslint-plugin-vue": "^8.0.3", "vue-cli-plugin-vuetify": "~2.5.8", "webpack-plugin-vuetify": "^2.0.0-alpha.0" }, "eslintConfig": { "root": true, "env": { "node": true }, "extends": [ "plugin:vue/vue3-essential", "eslint:recommended" ], "parserOptions": { "parser": "@babel/eslint-parser" }, "rules": {} }, "browserslist": [ "> 1%", "last 2 versions", "not dead", "not ie 11" ] }

     

    2. [ローカル]Vueでの開発

    ここではSPAの開発について説明します。今回はApiFrontでのSPAホスティングの解説に注力しています。
    そのため、Vueの実装はデプロイと動作確認が可能な最低限のものとし、実装の詳細などは省略しています。
    また、本チュートリアルはVue2からの移植版のため、Vue3の最新の実装方式(composition API)を踏襲しておりませんのでご注意ください。
     

    2-1. Vuetifyインストール

    簡易にボタンを扱うため、Vuetifyをインストールします。今回は3系に合わせてインストールします。

    bash
    # ./client 配下 $ vue add vuetify ... ? Choose a preset: Vuetify 3 - Vue CLI (preview)

    成功したら再度npm run serveしてください。

    先ほどのVueの画面からVuetifyがインストールされている画面に変更されていればOKです。

    2-2. axiosの追加

    vueでAPIリクエストのためaxiosを使用します。下記を実行してください。

    bash
    # ./client/src配下 npm install --save axios vue-axios

    インストール後、APIリクエスト処理用に client > src > plugins > api.jsを作成します。

    ./client/src/plugins/api.js

    jsx
    import axios from 'axios' export const api = axios.create({}) export default { install(app) { app.config.globalProperties.$api = api } }

     

    続いて、 client > src > main.jsを編集します。下記に置き換えてください。これでaxiosが実行可能になります。

    ./client/src/main.js

    jsx
    import { createApp } from 'vue' import App from './App.vue' import vuetify from './plugins/vuetify' import { loadFonts } from './plugins/webfontloader' // axiosのインポート import api from './plugins/api.js' loadFonts() createApp(App) .use(vuetify) // appに反映 .use(api) .mount('#app')

     

    2-3. ボタンの追加

    3点を変更して、ボタン/ボタンが呼び出す関数/response用変数を用意します。

    • 28行目以下にボタンを作るComponent配置
    • ボタンを押した時に動く関数getHealthCheck定義
    • responseの結果を格納するres_dataを用意

    ここではサンプルとして、地理情報を取得可能なAPIを叩いています。

    ./client/src/components/HelloWorld.vue

    html
    <template> <v-container> <v-row class="text-center"> <v-col cols="12"> <v-img :src="require('../assets/logo.svg')" class="my-3" contain height="200" /> </v-col> <v-col class="mb-4"> <h1 class="display-2 font-weight-bold mb-3"> Qmonus GUI開発チュートリアル </h1> <p class="subheading font-weight-regular"> チュートリアルの内容は下記を参照ください。 <br> <a href="https://developer.qmonus.net/document/casval/tutorial" >Tutorial page</a> </p> </v-col> </v-row> <v-row class="text-center"> <v-col> <v-btn @click=getHealthCheck()>GET HealthCheck</v-btn> <p class="subheading font-weight-regular" v-show="Object.keys(res_data).length"> {{res_data}} </p> </v-col> </v-row> </v-container> </template> <script> export default { name: 'TOP', data: () => ({ res_data : {} }), methods: { getHealthCheck: function () { this.$api.get("https://geoapi.heartrails.com/api/json", { params: { method: "getPrefectures", area: "関東" } }) .then(response => { this.res_data = response.data }) .catch(err => { console.error(err) }) } } } </script>

     

    GET HealthCheckボタンを押して下記のように出力されればOKです。


     

    2-4. 静的ページのビルドとGitHubへのpush

    以下のコマンドで静的ページをビルドします。


    # ./client配下 npm run build

    ビルドしたページはデフォルトでgitの追跡がされないため、 ./.gitignore に記載されている/distを消し、上記内容をGitにpushしてください。

     

    3. [Qmonus SDK Lab]SPAのデプロイ

    ここからはLabで環境をデプロイし、SPAのホスティングを行う部分について触れます。SPAのホスティングはApiFrontを用いて行います。
    ApiFrontはSPAホスティングの他、認証やAPIプロキシーなどの機能を持つアプリケーションです。
    詳細はApiFrontのドキュメントより閲覧可能です。

    なお、繰り返しになりますが本章はLabでのApiFrontのデプロイに注力し、デプロイの細かいパラメータやその他マイクロサービスについては詳しく触れません。
    Qmonus SDK Labでの環境作成手順と照らし合わせながら進んでいただければと思います。

    3-1. BaseInfo

    必要項目を入力ください。下記はサンプルになります。

    3-2. Git Repositories

    先ほどpushしたGitHubの情報を入力してください。

    3-3. Secrets

    不要です。

    3-4. MicroServices

    SDK / Portalは最新のバージョンを選びcheckしてください。今回両者はデプロイさえできれば良いので、それ以外の設定変更は不要です。

    3-4-1. SDK

    3-4-2. Portal

    3-4-3. ApiFront

    versionは最新ものを使用してください。以下を変更してください。
    また、以降の手順より表記される [repository-name] は使用しているリポジトリ名に置き換えてお読みください。

    • settings > +content.baseのパラメータ変更
    jsx
    "+content.base" : "/var/plugins/[repository-name]/client/dist",

    プロダクトの方針によっては、上記のLabでのbuild以外に2つのbuild方法があります。

     

    3-5. Check

    上記手順完了後デプロイします。json内のQmonus-ApiFrontが下記のような設定になっていれば問題ありません。

    json
    { "productName": "Qmonus-ApiFront", "versions": { "casval": "v23.1LTS-patch20230317", "redis": "6.2-alpine" }, "settings": { "arguments": { "+authentication.strategy": "qmonus", "+backend.proxypath": "/apis", "+backend.target": "http://apigw:9000", "+recommendedMode": "true", "+content.main": "index.html", "+content.base": "/var/plugins/[repository-name]/client/dist" }, "securityPolicy": "dev-api-policy", "buildSettings": { "pluginDir": "/var/plugins/[repository-name]/client" } } }

     

    3-6. デプロイの確認

    ApiFrontが青く光っていれば完了です。クリックしてログインを行ってください。

    作成したページが見えること、ボタン押下でレスポンスが見えればデプロイ完了です。

     

    4. 起動パラメータの設定と反映

    ApiFrontは起動パラメータという起動時にホスティングページの情報や認証、クッキー、APIプロキシーといった機能の設定を行うことができるパラメータを持ちます。
    本手順では実際に起動パラメータを設定し、ページのtitleDiscriptionを変更します。

    カスタマイズできる各起動パラメータの詳細は、3.起動パラメータよりご確認ください。

    起動パラメータの設定方法

    Labからdev環境をデプロイする場合、起動パラメータの設定は2通りで行うことができます。

    A. 起動パラメータをyaml形式で記述し、configファイルとして保存する
    B. LabよりMicroServices > ApiFront > settings > argments にprefix+を付けて追加する起動パラメータを記述

    同様のパラメータに対して値を設定した場合、B.の設定が優先されます。

    今回は上記の2パターンを両方設定し、優先されることを確認します。

     

    A. 起動パラメータをyaml形式で記述し、configファイルとして保存する

    4-1. configファイルの作成

    早速デプロイ用のファイルをトップディレクトリに作成しましょう。

    bash
    # [repository-name]配下 $ touch config.dev.yml

     

    4-2. 起動パラメータの記述

    今回はtitleとmetaタグのdescriptionを変更します。cofig.dev.ymlに下記を記述してGitHubにpushしてください。

    ./config.dev.yml

    yaml
    # コンテンツ設定 content : ## title title: "title ( set from yaml )" ## metaタグ設定 meta: description: "description ( set from yaml )"

     

    4-3. 既存設定の確認

    Labでデプロイし直す前に既存の設定を確認します。
    ApiFrontでデプロイしたSPAを開き、右クリックから「検証」を選択し、開発者ツールが開きます。
    <head>タグを展開するとtitleがclientであること、metaタグにdescriptionは存在しないことが確認できます。

     

    4-4. Labによるconfigファイルの指定と環境更新

    開発環境更新ボタンより環境更新を行います。今回変更が必要な点はMicroServices > ApiFront > settings > configPath になります。
    入力するにチェックを入れconfigpathにconfig.dev.ymlのpathを指定し、更新してください。

    yaml
    [repository-name]/config.dev.yml

     

    4-5. パラメータ反映の確認

    デプロイが完了したら、ページを開き反映されていることを確認します。

    開発者ツールより確認すると、titleタグ、metaタグ descriptionタグが確認できます。

    また、起動時に反映されたパラメータはPortalのCode editorを用いてログから確認できます。

    terminalで下記コマンドを実行してください。

    bash
    $ less casval/log

    デプロイの時刻に注意してログを探すと、下記のような起動時のパラメータ記述が見れると思います。
    ここで、contentを確認すると、titleとmetaが反映されていることがわかります。

    bash
    2023-04-05 06:16:05.356 [ LOG ] version 23.1.1 2023-04-05 06:16:05.356 [ LOG ] ################## CASVAL BOOT FILE 1 ################### 2023-04-05 06:16:05.357 [ LOG ] ## Config File Path : /var/plugins/[repository-name]/config.dev.yml 2023-04-05 06:16:05.357 [ LOG ] ## Config Base Path : /var/plugins/[repository-name] 2023-04-05 06:16:05.357 [ LOG ] ################## CASVAL Overwrite Configs ################### ...(中略)... 2023-04-05 06:16:05.368 [ LOG ] !!! Overwrite Config Parameter! 'backend.proxypath': /apis 2023-04-05 06:16:05.369 [ LOG ] !!! Overwrite Config Parameter! 'backend.target': http://apigw:9000 2023-04-05 06:16:05.370 [ LOG ] !!! Overwrite Config Parameter! 'recommendedMode': true 2023-04-05 06:16:05.370 [ LOG ] !!! Overwrite Config Parameter! 'content.main': index.html 2023-04-05 06:16:05.370 [ LOG ] !!! Overwrite Config Parameter! 'content.base': /var/plugins/[repository-name]/client/dist 2023-04-05 06:16:05.370 [ LOG ] !! recommendedMode [true] , The mode follows the default behavior, so the behavior may change when updating the versi on. 2023-04-05 06:16:05.371 [ LOG ] ################## CASVAL BOOT CONFIG ################### 2023-04-05 06:16:05.371 [ LOG ] { "version": "23.1.1", "appname": "Casval", ...(中略)... "content": { "enableHistory": true, "securityPolicy": "self", "securityPolicyOptions": { "additionalScriptSrc": "" }, "enableMultipage": false, "isFrame": false, "base": "/var/plugins/[repository-name]/client/dist", "main": "index.html", "delay": 0, "build": { "cwd": null, "command": null, "watch": { "path": null } }, "title": "title ( set from yaml )", "meta": { "description": "description ( set from yaml )" } }, ...(中略)...

     

    B. settings>argmentsにprefix+を付けて追加する起動パラメータを記述する

    4-6. 環境更新による起動パラメータ記述。

    こちらは上記環境の環境更新を行うことで動作を確認します。

    先ほどのconfigPathの設定はそのまま、ApiFront>settings>argumetnsに下記パラメータを追加します。

    この設定でcontent.titleのパラメータを上書きが可能になります。

    yaml
    "+content.title": "title_Qmonus_SDK_Lab"

     

    4-7. パラメータ反映の確認

    titleが先ほど設定した値に変更されていることが確認できます。

    また、configファイルで設定したmetaタグのdescriptionの存在も確認できます。

    起動パラメータの優先順位が configファイル < Labの設定 であることが、上記titleの値でわかります。
    共通の設定はconfigファイルで一括変更し、環境ごとに設定を変更したい場合はLabより設定することを推奨します。

     

    4-8. staging環境以降のconfigファイルについて

    staging以降は原則configファイルのみを用いて起動パラメータを管理する必要があります。
    staging以降の起動パラメータの例はこちらにあります。
    ※ 一般の方は閲覧できません。チュートリアル実行には影響ありませんが、staging以降の起動パラメータについて不明点があればサポートまでお問い合わせください。

    5. ローカルとQmonus SDK Labの疎通

    ローカルからLabでデプロイしたdev環境のAPIを叩く実装を紹介します。
    なお、ローカルとdev環境の疎通が不要な場合は本章の実行の必要はありません。

    PortalのProxyを利用して実現します、リクエストのフローは下記になります。

    1. ローカルからdev環境のPortalにログイン
    2. Portal Proxy用pathを取得
    3. APIリクエスト実行
    4. Portal Proxy用pathをリクエストpathに追加
    5. リクエスト先をPortal(target)に変更
    6. 認証がある状態でPortalに対してリクエスト
    7. PortalがプロキシーしてSDKへリクエストをフォワード
    8. SDKのAPIに着弾
       

    実現のため下記のステップを踏みます。
    5-1. Portal疎通用の記述
    5-2. GET /sessionの実行とsession情報及び環境情報の取得、保持
    5-3. PortalProxy pathの追加
    5-4. 動作確認

    5-1. Portal疎通用の記述

    5-1-1. qmonus-dev.jsの追加

    qmonus-dev.jsを作成します。qmonus-dev.jsは2つの機能があります。

    1. Portalへのログイン
    2. /apisで指定されたリクエストをPortalへ転送

    1.を用いてログインを行い、2.を用いてリクエストをPortalに転送します。
    ここで指定している/apisは5-3-1で取得するbackendのpathを参考にしています。
    qmonus-dev.jsには下記の記述をしてください。

    ./client/qmonus-dev.js

    jsx
    /************************************************************************* * * Qmonus SDK ApiFront * API proxy configuration tool for webpack * * version 1.1 * * Copyright(C) 2015-2023 NTT Communications, Inc. All rights reserved * author Yosuke Mizuno * *************************************************************************/ const axios = require('axios'); const https = require('https'); const bodyParser= require('body-parser') const agent = new https.Agent({ rejectUnauthorized: false }); const term = ( code , str )=>`\x1b[${code}m` + str + '\x1b[0m' const logger = { error: (msg) => console.error( `\n ${ term(41," FAIL ") } ${msg}` ), done: (msg) => console.log( `\n ${ term(32," SUCCESS ") } ${msg}` ), info: (msg) => console.info( `${ term(96," INFO ") } ${msg}` ) } /** * @param {*} target QmonusSDK-Portal endpoint * @param {*} username Qmonus account username * @param {*} password Qmonus account password * @param {*} options proxy options * @returns */ module.exports = ( { target , username , password , options={ headers:{} , inject:null } } )=>{ let Cookie = null; // Cached Cookie Value target = target.replace(/\/*$/, ""); if (!(typeof options.inject)) delete options.inject; return { allowedHosts: "all", async onAfterSetupMiddleware(devServer) { devServer.app.use(bodyParser.json()); try { let redirectRes = null; try { let params = new URLSearchParams(); params.append('username', username); params.append('password', password); redirectRes = await axios.post(`${target}/login`, params, { httpsAgent: agent, maxRedirects: 0 }) } catch (err) { redirectRes = err.response; } if (redirectRes.status != 302) throw new Error(`Login Redirect Fail , ${redirectRes.status} : ${redirectRes.statusText}`) Cookie = redirectRes.headers['set-cookie'][0]; // login check await axios.get(`${target}/session`, { httpsAgent: agent, maxRedirects: 0, headers: { 'cookie': Cookie } }) } catch (ex) { logger.error(`Qmonus Login Failed ${target} : [user:${username} , password:${password}]`) console.error(ex.stack) throw new Error("Qmonus Login Failed") } logger.done(`Qmonus Login successfully : Cookie ${Cookie}`) // get session proxy devServer.app.get("/session", async (req, res) => { axios.get(`${target}/session`, { httpsAgent: agent, withCredentials: true, headers: { 'cookie': Cookie } }) .then(ret => { logger.info(`${target}/session : ${ret.data.account_id}(${ret.data.role_name})`) res.json(ret.data) }) .catch(ex => { logger.error(`${target}/session : ${ex.message}`) res.status(ex.response.status).json(ex.response.data) }) }) // api proxy devServer.app.all("/apis/*", (req, res) => { let axiosParams = { method: req.method, url: `${target}${req.path}`, headers: { ...req.headers, ...{ 'cookie': Cookie }, ...(options.headers || {}) }, params: req.query, data: req.body }; if (options.inject) axiosParams = { ...axiosParams, ...options.inject(axiosParams) }; logger.info(`${ axiosParams.method } ${axiosParams.url} `) console.debug(` headers : `, axiosParams.headers) console.debug(` params : `, axiosParams.params) if (axiosParams.method == "GET") delete axiosParams.data; else console.debug(` data : `, axiosParams.data) return axios({ ...axiosParams, httpsAgent: agent, maxRedirects: 0, withCredentials: true }) .then(ret => { logger.info(`${ axiosParams.method } ${axiosParams.url} >> ${ret.status} : [server:${ret.headers["server"]} , content:${ret.headers["content-type"]}]`) res.status(ret.status) Object.keys(ret.headers).forEach(function (k) { switch (k.toLowerCase()) { case "set-cookie": case "cookie": case "content-length": break; default: res.set(k, ret.headers[k]) } }) res.json(ret.data) }) .catch(ex => { logger.error(`${ axiosParams.method } ${axiosParams.url} >> ${ex.response.status} : [${ex.message}]`) res.status(ex.response.status).json(ex.response.data) }) }) } } }

     

    5-1-2. dev環境にログイン用のアカウントを追加

    Portalでのログイン用に事前にアカウントを作成しておいてください。
    今回アカウントに紐付けるロールはlab_guestとしています。
    SDK Portal / 4.API開発チュートリアル / Account Setting

    5-1-3. vue.config.jsにdevServerの記述を追加

    vue.config.jsを編集し、ローカル実行時にqmonu-dev.jsが起動するようにします。
    また、編集例に含まれる以下の記述は作業環境に合わせて修正してください。

    • {デプロイした環境のPortalのURL}
    • {作成したアカウントのusername}
    • {作成したアカウントのpassword}

    ./client/vue.config.js

    jsx
    const { defineConfig } = require('@vue/cli-service') const qmonus = require("./qmonus-dev.js")({ target : "{デプロイした環境のPortalのURL}" , username : "{作成したアカウントのusername}" , password : "{作成したアカウントのpassword}", }) module.exports = defineConfig({ transpileDependencies: true, devServer : { host: 'localhost', port: 8080, open: false, ...qmonus , }, pluginOptions: { vuetify: { // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader } } })

     

    5-2. session情報の取得

    PortalやApiFrontに対して、GET /sessionを実行することでユーザや環境の情報が取得可能です。
    ApiFrontの/sessionのResponseは4.APIにあります。

    今回はランディング前にPortalに対してGET /sessionを実行し、Response BodyからPortalのプロキシー用のpath(session.backend)を取得します。
    取得したプロキシー用のpathをstoreに保存し、pathをリクエスト時に追加する仕組みを実現します。

     

    5-2-1. Vuexの導入

    session情報の保持のためにVuexを導入し、store機能を使用可能にします。下記のコマンドを実行してください。

    jsx
    # ./client/src配下 npm install vuex@next

     

    5-2-2. storeの記述を追加

    main.jsにstoreのインポートを記述します。
    ./client/src/main.js

    jsx
    import { createApp } from 'vue' import App from './App.vue' import vuetify from './plugins/vuetify' import { loadFonts } from './plugins/webfontloader' // axiosのインポート import api from './plugins/api.js' import { store } from './store/index.js' loadFonts() createApp(App) .use(store) .use(vuetify) // appに反映 .use(api) .mount('#app')

    client > src > store > index.jsとなるようにフォルダとファイルを作成します。 今回はsessionの保存を記述します。

    ./client/src/store/index.js

    jsx
    import { createStore } from 'vuex' export const store = createStore({ state: { session: null, }, getters:{ session(state){ return state.session || {} } }, mutations: { session( state , session ){ state.session = session } }, actions: { } })

     

    5-2-3. api.jsにgetSessionの追加

    api.jsに/sessionを実行する関数getSessionを作成し、storeにresponseを保持します。

    ./client/src/plugins/api.js

    jsx
    import axios from 'axios' import { store } from '../store/index.js' export const api = axios.create({}) const getSession = async (reflesh=false)=>{ return new Promise((resolve,reject)=>{ const getSessionImpl = (retry)=>{ axios.get( `/session` , { params : retry ? { reflesh:true } : undefined } ) .then(res=>{ store.commit("session" , res.data ) resolve(res.data); }) .catch(ex=>{ if( !retry ) return getSessionImpl(true) store.commit("error" , ex ) reject(ex) }) } getSessionImpl(reflesh) }) } export default { getSession : getSession , install(app) { app.config.globalProperties.$api = api } }

     

    5-2-4. main.jsの変更

    main.jsにstoreとgetSessionを叩く記述を追加します。これによりランディング時にsessionが実行されます。

    ./client/src/main.js

    jsx
    import { createApp } from 'vue' import App from './App.vue' import vuetify from './plugins/vuetify' import { loadFonts } from './plugins/webfontloader' // axiosのインポート import api from './plugins/api.js' import { store } from './store/index.js' loadFonts() ;(async ()=>{ // Get session API await api.getSession() createApp(App) .use(store) .use(vuetify) // appに反映 .use(api) .mount('#app') })();

     

    5-3. PortalProxy pathの追加

    5-3-1. api.jsにpath情報追加機能を記載

    api.jsのapi.interceptors.request.useにてconfig.baseURLにsessionで取得したbackendのpath情報を追加するように変更します。
    これによりリクエストpathにsessionで取得したpath情報が追加された状態でdevServerの処理に遷移します。

    ./client/src/plugins/api.js

    jsx
    import axios from 'axios' import { store } from '../store/index.js' export const api = axios.create({ }) api.interceptors.request.use( (config) => { // Portalの場合は/apis/axis/proxy // ApiFrontの場合は/apis config.baseURL = store.getters.session.backend; return config }, (error) => { return Promise.reject(error) } ) api.interceptors.response.use( (response) => { return response }, (error) => { return Promise.reject(error) } ) const getSession = async (reflesh=false)=>{ return new Promise((resolve,reject)=>{ const getSessionImpl = (retry)=>{ axios.get( `/session` , { params : retry ? { reflesh:true } : undefined } ) .then(res=>{ store.commit("session" , res.data ) resolve(res.data); }) .catch(ex=>{ if( !retry ) return getSessionImpl(true) store.commit("error" , ex ) reject(ex) }) } getSessionImpl(reflesh) }) } export default { getSession : getSession , install(app) { app.config.globalProperties.$api = api } }

     

    5-4. 動作確認

    5-4-1. SDKへのAPIリクエスト

    HelloWorld.vueにリクエストの内容を記述します。今回は環境で自動でデプロイされる監視用API、/healthcheck/ref/versionsに対してリクエストを実行します。
    先ほどのgetHealthCheckのpathとparamsを書き換えます。先ほどのconfig.baseURLのインジェクション機能のおかげで、ここでの記述は純粋なAPIpathである/healthcheck/ref/versions とすることができています。

    ./client/src/components/HelloWorld.vue

    html
    <template> <v-container> <v-row class="text-center"> <v-col cols="12"> <v-img :src="require('../assets/logo.svg')" class="my-3" contain height="200" /> </v-col> <v-col class="mb-4"> <h1 class="display-2 font-weight-bold mb-3"> Qmonus GUI開発チュートリアル </h1> <p class="subheading font-weight-regular"> チュートリアルの内容は下記を参照ください。 <br> <a href="https://developer.qmonus.net/document/casval/tutorial" >Tutorial page</a> </p> </v-col> </v-row> <v-row class="text-center"> <v-col> <v-btn @click=getHealthCheck()>GET HealthCheck</v-btn> <p class="subheading font-weight-regular" v-show="Object.keys(res_data).length"> {{res_data}} </p> </v-col> </v-row> </v-container> </template> <script> export default { name: 'TOP', data: () => ({ res_data : {} }), methods: { getHealthCheck: function () { this.$api.get("/healthcheck/ref/versions", { }) .then(response => { this.res_data = response.data }) .catch(err => { console.error(err) }) } } } </script>

     

    5-4-2. 動作確認(ローカル)

    この状態でnpm run serveを実行してください。開発者ツールからGET /sessionが200で帰っていることが確認できます。
    また、ボタンを押下することでGET /healthcheck/ref/version も200で帰り、bodyが出力できていることが確認できます。

    5-4-3. 動作確認(dev環境)

    npm run buildを実行した後、GitHubにpushしdev環境を更新してください。
    ApiFrontでホスティングしたページでも同様にGET /sessionGET /healthcheck/ref/version が200で帰っていることを確認できます。

    図より、ApiFrontでホスティングしたページでは下記にリクエストを投げています。


    GET /apis/healthcheck/ref/version

    このAPIリクエストがSDKに着弾するのは、ApiFrontでのプロキシー機能を使用しているからです。
    backend.proxypathで設定したpathのリクエストに対して、targetへリクエストをフォワードするdevServerと似た機能になります。
    プロキシの設定は起動パラメータのbackendで指定可能であり、Labからデプロイ時はデフォルトで下記が埋め込まれています。
    code editorより起動パラメータの値を確認すると下記になっていることが確認できます。

    bash
    ..(中略).. 2023-04-10 07:22:16.780 [ LOG ] !!! Overwrite Config Parameter! 'backend.proxypath': /apis 2023-04-10 07:22:16.783 [ LOG ] !!! Overwrite Config Parameter! 'backend.target': http://apigw:9000 ..(中略).. "backend": { "product": "axis", "proxyTimeout": 60000, "requestInjection": { "delay": 0, "script": null, "headers": {} }, "responseInjection": { "delay": 0, "script": null }, "proxypath": "/apis", "target": "http://apigw:9000" }, ..(中略)..

    起動パラメータの詳細は3.起動パラメータより、API Proxyを参照ください。

     

    6. FrontTesterを用いたGUI試験

    GUI試験は納品物の品質保証の観点からも必須の試験となります。Qmonus SDKでは、結合試験、End to End(E2E)試験を見越したGUI試験ツールとしてFrontTesterを提供しています。
    ここではFrontTesterを用いたGUI試験について紹介します。

     

    6-1. FrontTesterについて

    Qmonus FrontTesterはApiFront及びその他ブラウザに対して、ヘッドレスでGUIの単体試験を行うツールです。Portalから実行可能となっており、Qmonus SDKではFrontTesterを用いたGUI試験を推奨します。
    下記は画面イメージです。試験の進捗やログ、スクリーンショットが確認可能なモニタリング画面と、コードの記述が可能なエディタが存在します。

    詳細は Qmonus Documents / FrontTester /をご覧ください。

    FrontTesterのメリットは

    • APIリクエストで試験実行が可能であり、CIの利用や外部ツールからの試験実行が可能
    • 試験パラメータと試験シナリオが分離されており、GUI単体試験からE2E試験、CIなど様々な用途に対応可能
    • Portal上で実行・モニタリングが可能なため、Qmonus SDKを用いた一気通貫な開発が可能

    が挙げられます。

    今回作成したSPAを実際に試験するFrontTesterのチュートリアルを掲載しておりますので、実行いただくことを推奨します。

    6.リリースノート