or1ko's diary

日々を書きます

transparentネットワークを使う方法

デフォルトで存在するnatネットワークを使って、-pオプションでポートを指定して、
EXPOSEも設定したのだが、うまくつながらなかった。
色々調べたけれど、さっぱりわからなかった。
natにこだわりがあるわけでないので、
natでなくて、物理ネットワークをそのままつなげる方法を
試したところ、うまくいったので、その方法をメモする。

・まずは、transparentのネットワークを作成

docker network create -d transparent transparent

・そのネットワークを指定して起動する

docker run --net=transparenet ... <省略>

この方法に切り替えて、すぐにできたので、はよ切り替えればよかった。

下記を参考にした。
Windows コンテナーのネットワーク | Microsoft Docs

Excelファイルの操作(ImportExcel)

以前よりCOMを使ったexcelの操作方法は知っていたのだが、
覚えられるほど簡単ではなかったのであまり使かうことはなず、
csvファイルで出力し、手作業でxlsやxlsxファイルに変換することが多かった。

先日、PowerShellGetの存在をしり、ImportExcelという
モジュールが提供されていることを知って、結構気になっていて、
調べてみることにした。

PowerShell Gallery | ImportExcel 3.0.1

  • gciの結果をexcelファイルへエクスポート
gci | excel-export test.xlsx

期待通りに出力される。

以上

ConvertTo-*コマンドの動作

ConvertTo-で始まるPowerShellのコマンドはいくつかあるので、動作確認結果。
ConvertTo-Xmlはちょっとだけ期待はずれ。
最初はcsv

> ([PSCustomObject]@{id=1;name="Jhon"},[PSCustomObject]@{id=2;name="Tom"}) | ConvertTo-Csv
#TYPE System.Management.Automation.PSCustomObject
"id","name"
"1","Jhon"
"2","Tom"

次にjson

> ([PSCustomObject]@{id=1;name="Jhon"},[PSCustomObject]@{id=2;name="Tom"}) | ConvertTo-Json
[
    {
        "id":  1,
        "name":  "Jhon"
    },
    {
        "id":  2,
        "name":  "Tom"
    }
]

XML。デフォルトはxmlのオブジェクトになってしまうのでstringで出力。型情報も除去する。
Propertyでなく、idやnameにしたいが、方法は分からなかった。

> ([PSCustomObject]@{id=1;name="Jhon"},[PSCustomObject]@{id=2;name="Tom"}) | ConvertTo-Xml -As string -NoTypeInformation
<?xml version="1.0" encoding="utf-8"?>
<Objects>
  <Object>
    <Property Name="id">1</Property>
    <Property Name="name">Jhon</Property>
  </Object>
  <Object>
    <Property Name="id">2</Property>
    <Property Name="name">Tom</Property>
  </Object>
</Objects>

ちなみにHTMLは下記の通り。

> ([PSCustomObject]@{id=1;name="Jhon"},[PSCustomObject]@{id=2;name="Tom"}) | ConvertTo-Ht
ml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>HTML TABLE</title>
</head><body>
<table>
<colgroup><col/><col/></colgroup>
<tr><th>id</th><th>name</th></tr>
<tr><td>1</td><td>Jhon</td></tr>
<tr><td>2</td><td>Tom</td></tr>
</table>
</body></html>

Xmlを手軽に処理するならPowerShell

xmlのデータを集計など処理したい場合はPowerShellがおすすめ。
[xml]を使えば、解析してスクリプト上で簡単に扱えるようになる。

例えば、下記のような呼び出し方。
xmlファイルのhoge.fooという値にアクセスできる。

[xml]$(Get-Content *.xml -Encoding utf-8 -Raw).hoge.foo 

配列のshuffle

以前、shuffleをPowerShellで実装したが冗長だっため、再び調べてみたら、すごく簡単な方法が見つかった。

配列のshuffle - or1ko's diary

下記のページに記載されている。
Get-Random

どうも -Count パラメータを指定すると、入力をshuffleするようだ。

PS > 1..10 | Get-Random -Count ([int]::MaxValue)
6
7
5
2
4
1
8
10
3
9

ちなみに、shuffleはリストをランダムでいくつか取得した際に使うため、たまに使いたくなる。

Outlook の Web APIを呼び出す例

Outlook の Web APIを呼び出すサンプルコードを書いた。

起動して http://localhost:8080/ にアクセスすると、
OAuth2のためにOutlookに転送され、許可すると、
http://localhost:8080に戻ってきて、
メッセージのタイトル一覧を表示する。

Outlook の Web APIを呼び出す方法は下記に記載あり。OAuth2でアクセストークンを取得し、APIを呼び出す。
Get Started with the Outlook REST APIs - Outlook Dev Center


使用したパッケージ
- base >= 4.7 && < 5
- Spock >= 0.11
- mtl
- text
- hoauth2
- http-types
- http-client
- bytestring
- http-client-tls
- aeson

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Web.Spock
import Web.Spock.Config
import Network.OAuth.OAuth2

import Control.Monad
import Control.Monad.Trans
import Data.IORef
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString as B
import qualified Network.HTTP.Types.URI as URI
import qualified Network.HTTP.Types.Header as Header
import qualified Network.HTTP.Client as HTTPClient
import qualified Network.HTTP.Client.TLS as TLS
import qualified Data.Aeson as Aeson


data MySession = EmptySession
data MyAppState = DummyAppState (IORef Int)

main :: IO ()
main =
    do ref <- newIORef 0
       spockCfg <- defaultSpockCfg EmptySession PCNoDatabase (DummyAppState ref)
       runSpock 8080 (spock spockCfg app)

oauth2Param :: OAuth2
oauth2Param = OAuth2 {
  oauthClientId = "<client_id>",
  oauthClientSecret = "<client_secret>",
  oauthOAuthorizeEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
  oauthAccessTokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token",
  oauthCallback = Just "http://localhost:8080/"
}

scope = "https://outlook.office.com/mail.read"

redirectHotmail = redirect $ TE.decodeUtf8 $ appendQueryParam (authorizationUrl oauth2Param) [("scope", scope)]

getAccessToken code =
    do man <- HTTPClient.newManager TLS.tlsManagerSettings
       at <- fetchAccessToken man oauth2Param $ TE.encodeUtf8 code
       return $ TE.decodeUtf8 $ either BL.toStrict accessToken at

mail = "https://outlook.office.com/api/v2.0/me/messages"

mailparam = [
  ("$select", Just "Subject,From,ReceivedDateTime"),
  ("$top", Just "25"),
  ("$orderby", Just "ReceivedDateTime DESC")]

getMailBox :: T.Text -> IO BL.ByteString
getMailBox accessToken =
    do
      man <- HTTPClient.newManager TLS.tlsManagerSettings
      requestUrl <- HTTPClient.setQueryString mailparam <$> HTTPClient.parseRequest mail
      let request = requestUrl { HTTPClient.method = "GET",
                                 HTTPClient.secure = True,
                                 HTTPClient.requestHeaders = [(Header.hAuthorization, B.append "Bearer " $ TE.encodeUtf8 accessToken)]}
      res <- HTTPClient.httpLbs request man
      return $ HTTPClient.responseBody res

data MailMessage = MailMessage {
  subject :: T.Text
} deriving Show

instance Aeson.FromJSON MailMessage where
    parseJSON (Aeson.Object v) = MailMessage <$> (v Aeson..: "Subject")
    parseJSON _          = mzero

data MailBox = MailBox {
  value :: [MailMessage]
} deriving Show

instance Aeson.FromJSON MailBox where
    parseJSON (Aeson.Object v) = MailBox <$> (v Aeson..: "value")
    parseJSON _          = mzero

getMailMessages code = liftIO (getAccessToken code >>= getMailBox) >>= toHtml
  where
     toHtml body = html (maybe "" (T.unwords . map (T.append "<BR>" . subject) . value) (Aeson.decode body))

app :: SpockM () MySession MyAppState ()
app =
    get root $
        param "code" >>= maybe redirectHotmail getMailMessages