静的 Web ページ での WebSocket テスト

Design Automation API:WebSocket API ワークフロー で 3-legged  OAuth と  Das.WorkitemSigner ツールを使った Design Automation API の WebSocket API についてご紹介しました。

3-legged  OAuth と言うと、Web サーバーでコールバック URL を実装するのが一般的と思いますが、クラウアントとの直接コミュニケーションを目指した Design Automation API の WebSocket API では、少し矛盾があるように感じてしまいます。そこで、前回のブログ記事で「Implicit Grant も含め」としたのは、Web サーバー実装を伴う動的 Web ページではなく、静的 Web ページでの利用を意識したためです。

A diagram illustrating the interaction between users and a Forge application server, with icons representing different web browsers and cloud services, alongside logos for software like AutoCAD, Revit, Inventor, and 3DS Max.

常に同じコンテンツを配信することになる静的 Web ページ、極端な例えと思いますが、クラウド ストレージでパブリック共有してしまうと、Web ページの配信が出来てしまいます。ただ、クラウド ストレージ サービスの制限で、スクリプトを含む可能性のある HTML ファイルの共有やプレビューが制限されてしまい、すぐにテストが出来ないかと思います。

そんな時に便利なのが、Visual Studio Code(VS Code)に用意されている Live Server エクステンションです。

Visual Studio CodeのLive Serverエクステンションのインストールページのスクリーンショット

VS Code でフォルダを開いて静的 Web ページを選択後、マウスの右クリックで「Open with Live Server」を選択すると、ローカル コンピュータで既定のポート番号 5500 を利用したページのテストが出来るようになります。

Visual Studio Codeでのファイルブラウザーと'index.html'ファイルの右クリックメニューの様子。'Live Server'オプションが表示されている。

次のような HTML ページ(index.html)を用意して、Forge アプリの Callback URL を http://localhost:5500 しておけば、Das.WorkitemSigner ツールで署名した Activity と 3-legged  OAuth で、WebSocket による WorkItwm の起動と通知をテストすることが出来ます。

<html>
    <head>
        <title>DA4A WebSocket Test</title>
        <link type=”text/css” rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css”&gt;
        <script type=”text/javascript” src=”https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js”></script&gt;
        <script type=”text/javascript” src=”https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js”></script&gt;
        <script type=”text/javascript” src=”https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js”></script&gt;
    </head>
    <body onload=”MyStuff.onLoad()”>
        <script>
            var MyStuff = {
                “SOCKET”: “”,
                “access_token”: “”,
                “LogIn”: [“Log In”, “Logged In”],
                “onLoad”: function () {
                    console.log(“onLoad”)

                    var url = new URL(window.location.href.replace(‘#’, ‘?’))
                    var query_string = url.search
                    var search_params = new URLSearchParams(query_string)
                    MyStuff.access_token = search_params.get(‘access_token’)
                    let logInButton = document.getElementById(“LogIn”)
                    logInButton.innerText = (MyStuff.access_token) ? MyStuff.LogIn[1] : MyStuff.LogIn[0]

                    console.log(MyStuff.access_token)
                },
                “getUserInfo”: function () {
                    console.log(“getUserInfo”)

                    if (MyStuff.access_token === “”)
                        return

                    fetch(‘https://developer.api.autodesk.com/userprofile/v1/users/@me&#8217;, {
                        headers: {
                            ‘Authorization’: `Bearer ${MyStuff.access_token}`
                        }
                    })
                    .then(res => res.text())
                    .then(data => {
                        let json = JSON.parse(data)
                        let pretty = JSON.stringify(json, null, 2)
                        MyStuff.showInfo(pretty)
                        console.log(data)
                    })
                },
                “logIn”: function () {
                    console.log(“logIn”)
                    let clientId = <Client Id>
                    let scopes = “code:all”
                    let redirectUri = encodeURI(“http://localhost:5500&#8221;)
                    window.open(`https://developer.api.autodesk.com/authentication/v1/authorize` +
                        `?response_type=token&client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scopes}`, “_self”)
                },
                “doit”: function () {
                    var msg = JSON.stringify(
            {
                “action”: “post-workitem”,
                “data”:
                {
                    “activityId”: “Autodesk_Japan.Hello+dev”,
                    “signatures”: {
                        “activityId”: <署名済 Activity Id> 
                    }
                },
                “headers”:
                {
                    “Authorization”: “Bearer ” + MyStuff.access_token
                }
            })
        console.log(msg)
        SOCKET.send(msg)

        SOCKET.onmessage = function (event) {
            console.log(“通知” + event.data)
            $(“#message”).text(event.data)
        }

        SOCKET.onerror = function (error) {
            console.log(“エラー” + error.data)
            $(“#message”).text(error.data)
        }
                },
                “connect”: function () {
                    SOCKET = new WebSocket(‘wss://websockets.forgedesignautomation.io’)
                    SOCKET.onopen = function (event) {
                        console.log(“接続” + event.data)
                        $(“#message”).text(“接続”)
                    }
                },            
                “disconnect”: function () {
                    SOCKET.close()
                    SOCKET.onclose = function () {
                        console.log(“切断”)
                        $(“#message”).text(“切断”)
                    }
                }
            }            
        </script>
        <button id=”LogIn” class=”btn btn-outline-primary ml-3 my-2″ onclick=”MyStuff.logIn()”>Log In</button>
        <p></p>
        <button id=”connect” class=”btn btn-outline-dark ml-3 my-2″ onclick=”MyStuff.connect()”>WebSocket 接続</button>
        <button id=”doit” class=”btn btn-outline-dark ml-3 my-2″ onclick=”MyStuff.doit()”>WorkItem 実行</button>
        <button id=”disconnect” class=”btn btn-outline-dark ml-3 my-2″ onclick=”MyStuff.disconnect()”>WebSocket 切断</button>
        <p></p>
        <label id=”message” class=”ml-3 my-2″ style=”width:90%”></label>
    </div>
    </body>
</html>
WebSocket API設定画面のスクリーンショット。アプリ情報が表示されており、Client ID、Client Secret、App Name、Description、Callback URLが記載されている。

実際の Forge アプリは、Forge Viewer や他のサーバー実装を使った動的 Web ページを利用することが多いので、あまり利用する機会はないかもしれませんが、知っておくと便利です。

WebSocket Testページに表示されたボタン群、ログインボタン、WebSocket接続、WorkItem実行、WebSocket切断を表示しているアニメーションGIF

By Toshiaki Isezaki